Re: eval order of args to a sub
by ikegami (Patriarch) on May 30, 2007 at 19:56 UTC
|
Perl evaluates the arguments from left to right (according to the associtivity of the list seperator).
>perl -le"$i=3; print($i+0, ++$i, $i+0);"
344
However, because Perl passes all arguments by reference, the order of operation sometimes *appears* unclear.
>perl -le"$i=3; print($i, ++$i, $i);"
444
The first snippet is similar to
# $i=3; print($i+0, ++$i, $i+0);
$i = 3;
do {
local @_;
$anon0 = $i+0; alias $_[0] = $anon0;
$i = $i+1; alias $_[1] = $i;
$anon1 = $i+0; alias $_[2] = $anon1;
&print
};
The second snippet is similar to
# $i=3; print($i, ++$i, $i);
$i = 3;
do {
local @_;
$i; alias $_[0] = $i;
$i = $i+1; alias $_[1] = $i;
$i; alias $_[2] = $i;
&print
};
Can you guess what the following prints?
perl -le"$i=3; sub { $_[1]++; print @_ }->($i+0, ++$i, $i+0, $i);"
| [reply] [d/l] [select] |
|
That's very interesting. I could never have guessed that output before this thread. The creation of anonymous values (for lack of a better term) for some expressions makes me want to do some obfuscation...
| [reply] |
|
All scalar variables are really only references to SVs. (There's a similar relation between arrays and AVs, hashes and HVs, etc.) Sometimes Perl creates SVs to which no variable refers, and it's possible to create more than one variable that refers to the same SV.
+------------+ references (1:1) +------------+
| Lexical or | -----------------------> | |
| Package | | SV |
| Scalar Var | referenced by (1:N) | |
+------------+ <----------------------- +------------+
The "anonymous values" are simply SVs.
| [reply] [d/l] |
|
thanks for that explanation, I experimented a bit with that behaviour and found this a fun example:
perl -le'$i=3; print($i, $i++, $i);'
434
perl -le'$i=3; print($i, $i+1, $i);'
343
| [reply] |
|
- Can you guess what the following prints?
- perl -le"$i=3; sub { $_1++; print @_ }->($i+0, ++$i, $i+0, $i);"
That's a sneaky one! I was completely wrong: syntax error at -e line 1, near "="
PS: Just joking with you :)
| [reply] [d/l] |
|
$ perl -e'$i=3; sub { $_[0]++; print @_ }->(++$i, $i+0);'
54
$ perl -e'$i=3; sub { $_[1]++; print @_ }->($i+0, ++$i);'
35
| [reply] [d/l] |
Re: eval order of args to a sub
by Tanktalus (Canon) on May 30, 2007 at 18:55 UTC
|
I think the general rule of thumb is, "don't." As in, don't modify parameters to a function in the line that calls the function in such a way that could be confusing.
foo($i++); # ok
foo(++$i); # ok
foo($i,$i++); # not ok
foo($i++,$i); # not ok
foo($i++,++$i); # WTF?
If perl defined this, it would be one of very few languages that did so. And thus would still be confusing, IMO. It's not really that much extra work to avoid it. Use do if you have to:
my $flubber = do {
my @args;
push @args, $i++;
push @args, ++$i;
foo(@args)
}; # well defined: $_[1] == $_[0]+2
| [reply] [d/l] [select] |
|
Tanktalus++, best post in this thread: just "don't." And: avoid confusion.
In that context I'd like to point at Quantum Weirdness and the Increment Operator, again, for a lengthy discussion of why modifying a variable more than one time via auto-{inc,dec}rement in the same statement is a very bad idea.
--shmem
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
/\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
| [reply] |
Re: eval order of args to a sub
by dewey (Pilgrim) on May 30, 2007 at 18:48 UTC
|
From perlsub:
"...all functions are passed as parameters one single flat list of scalars, and all functions likewise return to their caller one single flat list of scalars."
In other words, all subs have only one argument, a list value. Since the constructor for list values is the comma-separated list of values, the question becomes 'in what order are the arguments to the list-context comma operator evaluated?' Frustratingly, the documentation doesn't seem to say...
My guess is that it's right to left in most implementations (due to the overloading of comma in scalar context) but that may not be dependable.
Later: It is in fact defined as left to right! Thanks, ikegami.
Later later: ummm, if this is crucial, please see discussion below. There is some disagreement.
| [reply] |
|
Your explanation makes sense.
The order of evaluation of the comma operator is defined as right-to-left. update2: or maybe not. see discussion below
see Terms and List Operators (Leftward)
update: I just tested this, and then I remembered that function arguments are aliases. Which throws the whole thing off.
perl -e'$i=1;sub a{print "@_"}; a($i--,++$i)'
| [reply] [d/l] |
|
I think the moral of the story is "understand what you are passing to your functions".
It appears that most ways that one might modify a variable in a function call will result in an alias to the variable being passed. Post-increment seems to be unique in that it creates an anonymous value.
C:\>perl -e "sub foo{ print join qq/\n/, map { \$_ .' - ' . $_ } @_ }
+foo($i=1,++$i,$i++,$i+1,$i+=1,$i);
SCALAR(0x183059c) - 4
SCALAR(0x183059c) - 4
SCALAR(0x225f7c) - 2
SCALAR(0x18305fc) - 4
SCALAR(0x183059c) - 4
SCALAR(0x183059c) - 4
| [reply] [d/l] |
|
|
|
Hmmm... I can't see where in Terms and List Operators (Leftward) the comma operator's order of evaluation is given... it mostly seems to concern term/parenthetical precedence. Am I missing something obvious?
Also, I don't understand your `update' remark. Your code produces 1 1, as I would have expected... could you clarify?
| [reply] [d/l] |
|
|
|
|
|
In other words, all subs have only one argument, a list value.
I think this is misleading. There is a list of arguments, pushed onto the stack and popped off (or accessed directly). There may be a single value in the list of arguments, but to talk about a list as if it is a single argument is... misleading.
| [reply] |
Re: eval order of args to a sub
by Trizor (Pilgrim) on May 30, 2007 at 18:55 UTC
|
Arguments are passed to subs as a list. if you have foo($arg1,@moargs) it will recieve @_ = ($arg1,@moargs). In list context list elements are placed in the list in order. However prototypes can change the way the arguments are evaluated. foo (\@\%) causes perl to treat foo(@argar,%arghs) as foo(\@argar,\%arghs).
This is all explained in perlsub which draws on information in perlfunc and perlop.
| [reply] [d/l] [select] |
Re: eval order of args to a sub
by swampyankee (Parson) on May 30, 2007 at 19:22 UTC
|
Evaluation order of arguments to a subroutine? One in a module, an intrinsic, or what?
Perl passes arguments to a subroutine as a list (see perlsub); the order in which the arguments are processed is dependent on how the subroutine is written. So, unless you've got access to the routine's source (or the routine is properly documented), I would not assume any particular order (although it's probably left to right, as mentioned in Re^7: finding number of contiguous letters by blazar).
Also, with my limited imagination, I can't conceive of a case where this matters and it's somewhere in the realm of what I've been taught as "good coding practice;" it reveals too much about the sub's internals.
punctuation correction
emc
Any New York City or Connecticut area jobs? I'm currently unemployed.
There are some enterprises in which a careful disorderliness is the true method.
—Herman Melville
| [reply] |
|
I'm having trouble imagining a sub that controls the order of evaluation of its 'parameters'; that sort of control of execution feels like it requires macros. Could you give me an example? For instance, two subs first and second such that
first(print('a'), print('b')) # prints 'ab'
second(print('a'), print('b')) # prints 'ba'
To me, this whole thing feels like a misunderstanding. Each sub receives one list value as an argument. We're not passing in expressions to be evaluated or anything like that.
| [reply] [d/l] [select] |
Re: eval order of args to a sub
by andreas1234567 (Vicar) on May 31, 2007 at 09:33 UTC
|
Slightly OT perhaps, but the freely available chapter 9 of Perl Best Pratices (PDF) recommends:
Use a hash of named arguments for any subroutine
that has more than three parameters.
Named arguments should always be passed to a subroutine inside a single hash, like
so:
sub padded {
my ($arg_ref) = @_;
my $gap = $arg_ref->{cols} - length $arg_ref->{text};
my $left = $arg_ref->{centered} ? int($gap/2) : 0;
my $right = $gap - $left;
return $arg_ref->{filler} x $left
. $arg_ref->{text}
. $arg_ref->{filler} x $right;
}
# and then...
for my $line (@lines) {
$line = padded({ text=>$line, cols=>20, centered=>1, filler=>$SPAC
+E });
}
Andreas
-- | [reply] [d/l] [select] |
Re: eval order of args to a sub
by polettix (Vicar) on May 31, 2007 at 16:46 UTC
|
| [reply] |