[comp.lang.perl] Creating a reference to an array index

pem@frankland-river.aaii.oz.au (Paul E. Maisano) (03/17/90)

A short while ago I posted a request to see if anyone could think of
a way I could generate a reference to a particular index of an array.

For scalars it is easy; I have used code like the following to parse
command line args. The effect is: 'prog -p x y -u 1 2 -p z' results in
	$pvar == 'x y z ', $uvar == '1 2 '

while ($_ = shift) {
    /^-p$/ && (*vptr = *pvar, next);
    /^-u$/ && (*vptr = *uvar, next);
    $vptr .= "$_ ";
}

Anyway, if I have an array, say @ary = (1, 2, 3)
I want to be able to get a reference to an element of that array, so
that I can manipulate it independently of the array.

Randal mentioned that a foreach might work as in (something like):
	foreach $x ($ary[1]) {
	    # manipulate x and modify the array
	}
The foreach will only have this effect when an actual array is passed to the
foreach. So you can't really use this method.

I mentioned that a grep would work as in the following:
	grep( do {
		# manipulate $_ and modify the array
		},
	      $ary[1] );
This has the draw back of not letting you name the variable to something
meaningful.

So in my search for neatness (or any kludge that would work) I came up
with the following:
	
	grep(*vptr = *_, $ary[1])
	# manipulate $vptr and modify the array

It makes sense when you think about it. `_' contains the symbol table entry
for the element of the array which we then assign to `vptr'.
(Ok, ok, so I peeked at the source :-)

Now I am content and can sleep again at nights.
But I am too tired to work out how to generate "Just Another Perl Hacker,"
using it. Randal ? :-)

Here's a test fragment:

#!/usr/local/bin/perl 
@ary = (1, 2, 3);
grep(*vptr = *_, $ary[1]);
print "@ary\n";		# => 1 2 3
$vptr++;
print "@ary\n";		# => 1 3 3

------------------
Paul E. Maisano
Australian Artificial Intelligence Institute
1 Grattan St. Carlton, Vic. 3053, Australia
Ph: +613 663-7922  Fax: +613 663-7937
Email: pem@aaii.oz.au   UUCP: {uunet,mcsun,ukc,nttlab}!munnari!aaii.oz.au!pem

merlyn@iwarp.intel.com (Randal Schwartz) (03/18/90)

In article <1361@frankland-river.aaii.oz.au>, pem@frankland-river (Paul E. Maisano) writes:
| So in my search for neatness (or any kludge that would work) I came up
| with the following:
| 	
| 	grep(*vptr = *_, $ary[1])
| 	# manipulate $vptr and modify the array
[...]
| Now I am content and can sleep again at nights.
| But I am too tired to work out how to generate "Just Another Perl Hacker,"
| using it. Randal ? :-)

Something like this?

@ARGV=split(//,'Just another Perl hacker,');push(@x,'')while@x<@ARGV;
for$x($[..$#x){grep(*y=*_,$x[$x]);$y=shift;}print@x;

(Okay, so it's pretty simplistic...)  But it'd be much easier to do:

@ARGV=split(//,'Just another Perl hacker,');push(@x,'')while@x<@ARGV;
for$y(@x){$y=shift;}print@x;

So I still don't know why you want to alias an element like that. :-(

By the way, I tried to preextend the array with $#x=$#ARGV, and it
didn't work.  Why not?

print "Just another Perl hacker,"; # to make larry's code happy :-)
-- 
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
| merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Welcome to Portland, Oregon, home of the California Raisins!"=/

pem@frankland-river.aaii.oz.au (Paul E. Maisano) (03/18/90)

In article <1990Mar17.183039.21097@iwarp.intel.com>, merlyn@iwarp.intel.com (Randal Schwartz) writes:
> 
> So I still don't know why you want to alias an element like that. :-(

This method probably does not buy you anything that you can't do with
associative arrays or using eval and coding things a little differently.

I can only give you the reason I was originally looking for a technique
like this. Say I have an array of buffers, @current_buffer indexed by
$current_line and I want to use it in a subroutine. I don't think it would
be uncommon to see something like the following at the start:
	sub handle_input {
	    local($buf) = $current_buffer[$current_line];
	    ...
From now on I can use $buf instead of the (more informative) but extremely
verbose $current_buffer[$current_line].

This is fine and good, everything works well. I have about 5 to 10
references to $buf in my subroutine which I think is easier to read than
the alternative. But as soon as realize that I need to start modifying
the buffer it stops working since $buf is only a copy. This is not
drastic. I can either go back to using the verbose form everywhere or
simply copy $buf back into the real buffer before every place I return
from the subroutine.

The problem seemed to me that I was using local in a different way than was
intended. I was trying to use it as an aliasing mechanism which it is not.
Now I can say
	local(*buf) = &ref($current_buffer[$current_line)];
to get the effect I was trying to achieve.

For anyone interested &ref can be defined as follows:
	sub ref {
		local($tmp);
		grep($tmp = *_, $_[0]);
		$tmp;
	}
This routine could be made more general by returning an array of
references for each argument. Ie. you could write,
	local(*x, *y, *z) = &ref($x[1], $y[0], $z[2]);

You could even set things up so that two arrays share elements!!
I'd better stop right here.. I feel a madness coming on :-)

------------------
Paul E. Maisano
Australian Artificial Intelligence Institute
1 Grattan St. Carlton, Vic. 3053, Australia
Ph: +613 663-7922  Fax: +613 663-7937
Email: pem@aaii.oz.au   UUCP: {uunet,mcsun,ukc,nttlab}!munnari!aaii.oz.au!pem

pem@frankland-river.aaii.oz.au (Paul E. Maisano) (03/19/90)

In article <1362@frankland-river.aaii.oz.au>, I (Paul E. Maisano) write:
> For anyone interested &ref can be defined as follows:
> 	sub ref {
> 		local($tmp);
> 		grep($tmp = *_, $_[0]);
> 		$tmp;
> 	}
> This routine could be made more general by returning an array of
> references for each argument. Ie. you could write,
> 	local(*x, *y, *z) = &ref($x[1], $y[0], $z[2]);

The generalized version is:
    sub ref {
	local(@stab);
	grep(push(@stab,*_),@_);      # kludge to get at symtab entries
	wantarray ? @stab : $stab[0]; # contains the binary symtab data
    }

> You could even set things up so that two arrays share elements!!

Ignore that. As far as I can tell there is no way whatsoever in the current
version of perl to make an array element reference something else. I agree
that it's probably not of any real use anyway.
The reason I think it is impossible, is that the only way you can make
something reference another symbol in perl is to use the type globbing syntax. 
And you aren't allowed to do "*LVALUE = ..." only "*NAME = ...".
In other words, you can't name an array element; only the array.

BTW,
I would also like to see gethostname (and some version of timelocal) in perl.
[But I've already voted for these so it doesn't count, right!? :-]
 
------------------
Paul E. Maisano
Australian Artificial Intelligence Institute
1 Grattan St. Carlton, Vic. 3053, Australia
Ph: +613 663-7922  Fax: +613 663-7937
Email: pem@aaii.oz.au   UUCP: {uunet,mcsun,ukc,nttlab}!munnari!aaii.oz.au!pem