zapdos has asked for the wisdom of the Perl Monks concerning the following question:
Dear Monks, if I do what's in the code below, that's I don't enclose the left-hand side variables in parentheses like my ($a, $x, $y, $z) = foo() but instead do:
my $a, $x, $y, $z = foo()
Can you monks please explain step-by-step what computations will happen here?
Re: What's happening in this expression?
by haukex (Archbishop) on Oct 11, 2020 at 09:50 UTC
|
Can you monks please explain step-by-step what computations will happen here?
Under strict, none, because it will not compile unless $x, $y, and $z have been previously declared. Otherwise, B::Deparse helps:
$ perl -MO=Deparse,-p -e 'my $a, $x, $y, $z = foo()'
(my($a), $x, $y, ($z = foo()));
In other words, it's declaring a lexical $a, then $x and $y are simply sitting there, and then $z is assigned the return value of the function call foo() - although if that's not defined, that call will fail. See also Comma Operator. Turning on warnings gives some relatively helpful hints:
$ perl -w -e 'my $a, $x, $y, $z = foo()'
Parentheses missing around "my" list at -e line 1.
Useless use of a variable in void context at -e line 1.
Useless use of a variable in void context at -e line 1.
Name "main::x" used only once: possible typo at -e line 1.
Name "main::z" used only once: possible typo at -e line 1.
Name "main::y" used only once: possible typo at -e line 1.
Undefined subroutine &main::foo called at -e line 1.
| [reply] [d/l] [select] |
|
Thank you so much. What are the two warnings "Useless use of a variable in void context at -e line 1." referring to?
| [reply] |
|
What are the two warnings "Useless use of a variable in void context at -e line 1." referring to?
They're referring to $x and $y, because the statement as written above is in void context, so these variables are in void context too. I was intentionally a little vauge with my "simply sitting there" because it's possible for them to not be in void context, for example if this statement was the last thing in a sub (the context that the sub is called in is passed through):
use warnings;
use strict;
my ($x,$y,$z);
sub foo { qw/a b c d/ }
sub bar {
my $a, $x, $y, $z = foo()
}
my @x = bar();
my $r = bar();
This only produces the warning "Parentheses missing around "my" list". @x will contain the values (undef, undef, undef, "d"), because $a, $x, and $y are undef (nothing is assigned to them), while $z is assigned the return value of foo(), which means it is assigned "d" because my example sub foo is returning a list, and a list in scalar context evaluates to its last value. $r will contain "d" for the same reason. | [reply] [d/l] [select] |
|
|
|
>then $x and $y are simply sitting there
| [reply] |
Re: What's happening in this expression? (Updated)
by LanX (Sage) on Oct 11, 2020 at 12:42 UTC
|
> but instead do:
> my $a, $x, $y, $z = foo()
The precedence of = is higher than , !
Resulting evaluation:
(my $a), ($x), ($y), ($z = foo())
That's effectively the same like 4 separate statements.
my $a ; $x ; $y ; $z = foo() ;
Consequences
1. => scalar assignment to $z and scalar context for foo() call
2. => my declaration only for $a
Update
3. => $x and $y are undeclared and in void context
4. => strict will fail
In short:
DON'T!!!
| [reply] [d/l] [select] |
|
What about
my $a=$x=$y=$z = foo()
Here my attribute does not propogate like in case of comma. It applies only to $a, right ?
But now the fun starts
DB<3> use v5.10
DB<4> my $a=$x=$y=$z = 1
DB<5> say "|$a|$x|$y|$z|"
||1|1|1|
DB<6> unless( defined($a) ){ say "a is undefined"}
a is undefined
why $a remains uninitialized? | [reply] |
|
$ cat test.pl
use 5.032;
my $foo = 10;
say qq{foo: $foo}
$ perl -d test.pl
Loading DB routines from perl5db.pl version 1.57
Editor support available.
Enter h or 'h h' for help, or 'man perldebug' for more help.
main::(test.pl:2): my $foo = 10;
DB<1> x $foo
0 undef
DB<2> n
main::(test.pl:3): say qq{foo: $foo}
DB<2> x $foo
0 10
DB<3> my $foo = 20
DB<4> x $foo
0 10
DB<5> my $foo = 20; say qq{foo: $foo}
foo: 20
DB<6> x $foo
0 10
Simple rule of thumb I tend to follow is just don't use my (or state or our) from the debugger command line to try and affect anything outside of that immediate command line.
The cake is a lie.
The cake is a lie.
The cake is a lie.
| [reply] [d/l] [select] |
|
|
|
|
What about my $a=$x=$y=$z = foo()
$ perl -MO=Deparse,-p -e 'my $a=$x=$y=$z = foo()'
(my $a = ($x = ($y = ($z = foo()))));
The comma is left associative and the assignment is right associative. See perlop.
Here my attribute does not propogate like in case of comma.
No, it doesn't "propagate" with the comma either, as I showed.
| [reply] [d/l] |
|
Win8 Strawberry 5.8.9.5 (32) Sun 10/11/2020 21:47:54
C:\@Work\Perl\monks
>perl -Mwarnings
my $a=$x=$y=$z = 1;
print "|$a|$x|$y|$z|";
^Z
|1|1|1|1|
Win8 Strawberry 5.30.3.1 (64) Sun 10/11/2020 21:45:55
C:\@Work\Perl\monks
>perl -Mwarnings
use v5.10;
my $a=$x=$y=$z = 1;
say "|$a|$x|$y|$z|";
^Z
|1|1|1|1|
Update:
What about
my $a=$x=$y=$z = foo()
With respect to this specific statement:
Win8 Strawberry 5.8.9.5 (32) Sun 10/11/2020 21:48:09
C:\@Work\Perl\monks
>perl -Mstrict -Mwarnings
use Data::Dump qw(dd);
sub foo { return 9, 8, 7; }
my ($x, $y, $z);
my $a = $x = $y = $z = foo;
dd $a, $x, $y, $z;
^Z
(7, 7, 7, 7)
Same result under version 5.30.3.1.
Give a man a fish: <%-{-{-{-<
| [reply] [d/l] [select] |
|
| [reply] [d/l] |
Re: What's happening in this expression?
by jwkrahn (Monsignor) on Oct 12, 2020 at 06:09 UTC
|
In Perl, as in the C programming language, statements are separated with a semicolon. There are also times where statements can be separated with a comma, mostly used in a for ( i = 1, j = 10; i < 10; ++i, j += 4 ) { ... } expression.
In your case you have four separate statements, separated by commas instead of semicolons: i.e. my $a; $x; $y; $z = foo().
| [reply] [d/l] [select] |
Re: What's happening in this expression?
by zapdos (Sexton) on Oct 11, 2020 at 13:47 UTC
|
Thank you very much haukex, perlfan and LanX. | [reply] |
Re: What's happening in this expression?
by perlfan (Vicar) on Oct 11, 2020 at 09:55 UTC
|
perl -e 'sub foo {return (1,2,3,4)}; my $a, $x, $y, $z = foo(); print
+qq{$a, $x, $y, $z\n}'
, , , 4
perl -e 'sub foo {return (1,2,3,4)}; my ($a, $x, $y, $z) = foo(); prin
+t qq{$a, $x, $y, $z\n}'
1, 2, 3, 4
| [reply] [d/l] [select] |
|
| [reply] [d/l] |
|
Yes, that's why I always use characters ... but there is another trap lurking ...
Extra tip: never use the range operator to return a list.
One of the most annoying design flaws of Perl is the propagation of context to a subs returning statement. That's action at a distance...
And you really don't want a flip-flop when you expect a list...
DB<50> sub tst { "a".."d" }
DB<51> x tst() # list context => range
0 'a'
1 'b'
2 'c'
3 'd'
DB<52> p scalar tst() # scalar context => flip-flop
1E0
DB<53> x tst() # list context => WTF???
0 0
DB<54> x tst() # once flip-flop, always flip-flop
0 0
DB<55>
Workaround: reverse
DB<44> sub tst { reverse "a".."d" }
DB<45> x tst()
0 'd'
1 'c'
2 'b'
3 'a'
DB<46> p scalar tst()
dcba
DB<47> x tst()
0 'd'
1 'c'
2 'b'
3 'a'
DB<48>
And if don't like the order, reverse twice
DB<55> sub tst { reverse reverse "a".."d" }
update
another - uglier - alternative:
DB<59> sub tst { @{["a".."d"]} }
DB<60> x tst()
0 'a'
1 'b'
2 'c'
3 'd'
DB<61> p scalar tst()
4
DB<62> x tst()
0 'a'
1 'b'
2 'c'
3 'd'
DB<63>
| [reply] [d/l] [select] |
A reply falls below the community's threshold of quality. You may see it by logging in. |
|
|