my $ref1 = ['a','b','c'];
my $ref2 = ['d','e','f'];
my $refref = [$ref1, $ref2];
myfunc($refref);
print $ref1->[0];
sub myfunc {
my (@list) = @{[@{$_[0]}]};
print $list[0][0];
$list[0] = ['1','2','3'];
}
it prints 'aa' and not 'a1' because @{[(list of refs)]} has built
a copy of 'list of refs' protecting $ref1 from being altered by the assignment inside the funcion
my 2 cents... | [reply] [d/l] [select] |
sub foo {
my @foo = @{[@{$_[0]}]};
# etc
}
over
sub foo {
my @foo = @{$_[0]};
# etc
}
In fact try this:
my $bar = ["hello", "world"];
foo($bar);
print "$bar->[0]\n";
sub foo {
# Make my copy.
my @foo = @{$_[0]};
# Try to mess up the first element.
$foo[0] =~ s/h/H/;
}
See? You get a private copy already. | [reply] [d/l] [select] |
I was trying to explain "what" that code does and not "why"
however you'r right: my (@list) = @{[@{$_[0]}]};
will just build an extra (useless) copy, i didn't verified it..
| [reply] [d/l] |
| [reply] [d/l] [select] |
i'd say the having the most correct answer is worth more
than 2 cents.
| [reply] |
Occam's razor! It's great!
[ar0n]
| [reply] |
I've never tried using @{[...]} outside of strings before but I'm assuming it does pretty much the same thing, which is briefly mentioned in perlref's Using References section ...
Here's a trick for interpolating a subroutine call into a string:
print "My sub returned @{[mysub(1,2,3)]} that time.\n";
The way it works is that when the @{...} is seen in the double-quoted string, it's evaluated as a block. The block creates a reference to an anonymous array containing the results of the call to mysub(1,2,3). So the whole block returns a reference to an array, which is then dereferenced by @{...} and stuck into the double-quoted string. This chicanery is also useful for arbitrary expressions:
print "That yields @{[$n + 5]} widgets\n";
| [reply] [d/l] [select] |
I can't tell you exactly how the dereferencing breaks down, but I can tell you that evalop() gets an array of arrrays, where each array element is a reference to a closure (I think I'm using the right term. If not, call them anonymous subroutines).
The basic theory behind demo_calc.pl is that it builds a list of operations, where each operation (numbers, function, whatever) is a closure. It, in effect, is building a TIL, or threaded interpeted language. Unlike a language like Forth, the thread only survives for the life of the operation (but that really doesn't matter).
I spent some time trying to use Data::Dumper to dump the thread, but because Data::Dumper doesn't know what to do with code refs, it really didn't break down well. The closest I came was the implementation below, which gives you a better feel for how it operates. It could be a little clearer, but I was starting to get frusterated with it all.
I didn't test with range operators or variables, but basic operations like 1/2+2 vs. 1/(2+2)/4, etc.
#! /usr/local/bin/perl -ws
use strict;
use Parse::RecDescent;
use Data::Dumper;
my %hashlist = ();
my $keycount = 0;
my $parse = Parse::RecDescent->new(<<'EndGrammar');
main: expr /\s*\Z/ { $item[1]->() }
| <error>
expr: /for(each)?/ lvar range expr
{ my ($vname,$expr) = @item[2,4];
my ($from, $to) = @{$item[3]};
sub { my $val;
no strict "refs";
for $$vname ($from->()..$to->())
{ $val = $expr->() }
return $val;
}
}
| lvar '=' addition
{ my ($vname, $expr) = @item[1,3];
sub { no strict 'refs'; $$vname = $expr->() }
}
| addition
range: "(" expr ".." expr ")"
{ [ @item[2,4] ] }
addition: <leftop:multiplication add_op multiplication>
{ my $add = $item[1]; my $ref = sub { ::evaler ($add); ::eval
+op $add }; ::push ('addevalop', $ref) }
add_op: '+' { my $ref = sub { print "Add...$_[0] + $_[1]\n"; $_[0]
++= $_[1] }; ::push ('add', $ref) }
| '-' { my $ref = sub { print "Sub...$_[0] - $_[1]\n"; $_[0]
+-= $_[1] }; ::push ('sub', $ref) }
multiplication: <leftop:factor mult_op factor>
{ my $mult = $item[1]; my $ref = sub { ::evaler ($mult); ::ev
+alop $mult }; ::push ('mulevalop', $ref) }
mult_op: '*' { my $ref = sub { print "Mul...$_[0] * $_[1]\n"; $_[
+0] *= $_[1] }; ::push ('mul', $ref) }
| '/' { my $ref = sub { print "Div...$_[0] / $_[1]\n"; $_[
+0] /= $_[1] }; ::push ('div', $ref) }
factor: number
| rvar
| '(' expr ')' { $item[2] }
number: /[-+]?\d+(\.\d+)?/ { my $ref = sub { $item[1] }; ::push ('n
+umber', $ref) }
lvar: /\$([a-z]\w*)/ { $1 }
rvar: lvar { my $ref = sub { no strict 'refs'; ${$item[1]} }
+; ::push ('rvar', $ref) }
EndGrammar
print "> ";
while (<>) {
print $parse->main($_), "\n\n> ";
}
#
# This allows us to 'name' the closures. We append an ever-increment
+ing number to the hash key so that
# multiple add, subtract, whatever operations won't overwrite the exi
+sting key. Remember that each
# operation is a standalone closure, and are not 'packed' down to one
+ common routine for each function.
#
sub evaler
{
my $arr = shift;
print "Eval ", join (', ', map {$hashlist {$_}} @$arr), "\n";
}
#
# Build the hashlist so we know what the closures refer to.
#
sub push
{
my ($name, $ref) = @_;
my $val = sprintf ("%s_%d", $name, $keycount++);
print "key=$ref, val=$val";
print ", number=", &$ref if ($name eq 'number');
print "\n";
$hashlist {$ref} = $val;
#
# Have to return the ref, because Parse::RecDescent expects it
#
return $ref;
}
sub evalop
{
print Data::Dumper->Dump ([\@_]);
my (@list) = @{[@{$_[0]}]};
my $val = shift(@list)->();
while (@list)
{
my ($op, $arg2) = splice @list, 0, 2;
$op->($val,$arg2->());
}
return $val;
}
--Chris
e-mail jcwren | [reply] [d/l] |
It looks to me like a mistake. I just wrote to Damian to ask about it. When I get an answer, I'll post.
| [reply] |
Damian Conway,
the original author of the code in question, says:
That's so baroque, I'm sure it must originally have been intentional,
though it's now clearly vestigial.
An earlier version probably did something like:
foreach (@{[@{$_[0]}]}) {...}
where the inline copy was essential, presumably to prevent
$_ iterating
the original arguments. And then I simply forgot to correct it when I
changed the structure.
I've changed it now. Thanks.
| [reply] [d/l] |