Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl - evaluation order of list elements, with assignment inside the list

According to Perldoc( https://perldoc.perl.org/perlop#Comma-Operator ):

Comma Operator

...

In list context, it's just the list argument separator, and inserts both its arguments into the list. These arguments are also evaluated from left to right.

In following code, I thought $n should be 1 but it became 2. What am I missing?

my $v = 1;
my $n = ( $v, $v = 2 )[0];
# I thought left "$v" would be evaluated (and return 1) first, then right "$v = 2" would.
say "n = $n";  # but output was 2
say "v = $v";  # 2

or

my $v = 1;
my @l = ( $v, $v = 2 );
say Dumper(\@l);   # [ 2, 2 ], not [ 1, 2 ]

even when I changed the order:

my $v = 1;
# my $n = ( $v, $v = 2 )[0];
my $n = ( $v = 2, $v )[1];
say "n = $n";  # still 2
say "v = $v";  # 2
like image 813
gypark Avatar asked Jan 29 '21 15:01

gypark


1 Answers

It does get evaluated left-to-right.

$ perl -MO=Concise,-exec -e'my $v = 1; my $n = ( $v, $v = 2 )[0];'
1  <0> enter v
2  <;> nextstate(main 1 -e:1) v:{
3  <$> const[IV 1] s                   \
4  <0> padsv[$v:1,3] sRM*/LVINTRO       > my $v = 1;
5  <2> sassign vKS/2                   /
6  <;> nextstate(main 2 -e:1) v:{
7  <0> pushmark s                                  \              \
8  <$> const[IV 0] s                               |              |
9  <0> pushmark s                                  |              |
a  <0> padsv[$v:1,3] s                    $v       > ( ... )[0]   |
b  <$> const[IV 2] s                   \           |              > my $n = ...
c  <0> padsv[$v:1,3] sRM*               > $v = 2   |              |
d  <2> sassign sKS/2                   /           |              |
e  <2> lslice sK/2                                /               |
f  <0> padsv[$n:2,3] sRM*/LVINTRO                                 |
g  <2> sassign vKS/2                                             /
h  <@> leave[1 ref] vKP/REFC
-e syntax OK

The catch is that the scalar associated with $v is placed on the stack, not the integer. You can see what's happening here:

use 5.014;
use warnings;

my @stack;

sub sassign {
   my $rhs = pop(@stack);
   my $lhs = pop(@stack);
   $$rhs = $$lhs;
   push @stack, $rhs;
}
                       # Stack     $v   $n
                       # -------   --   --
push @stack, \1;       # 1
push @stack, \my $v;   # 1,$v      !d
sassign();             # $v        1
@stack = ();           #           1

push @stack, \$v;      # $v        1

push @stack, \2;       # $v,2      1
push @stack, \$v;      # $v,2,$v   1
sassign();             # $v,$v     2

pop(@stack);           # $v        2          # Net result of slice.
push @stack, \my $n;   # $v,$n     2    !d
sassign();             # $n        2    2
@stack = ();           #           2    2

say $n;  # 2

If you prefer aliases over references:

use 5.014;
use warnings;
use experimental qw( refaliasing declared_refs );

my @stack;

sub sassign {
   my \$rhs = \pop(@stack);
   my \$lhs = \pop(@stack);
   $rhs = $lhs;
   \$stack[@stack] = \$rhs;
}
                            # Stack     $v   $n
                            # -------   --   --
\$stack[@stack] = \1;       # 1
\$stack[@stack] = \my $v;   # 1,$v      !d
sassign();                  # $v        1
@stack = ();                #           1

\$stack[@stack] = \$v;      # $v        1

\$stack[@stack] = \2;       # $v,2      1
\$stack[@stack] = \$v;      # $v,2,$v   1
sassign();                  # $v,$v     2

pop(@stack);                # $v        2          # Net result of slice.
\$stack[@stack] = \my $n;   # $v,$n     2    !d
sassign();                  # $n        2    2
@stack = ();                #           2    2

say $n;  # 2
like image 86
ikegami Avatar answered Nov 15 '22 18:11

ikegami