【问题标题】:Perl - evaluation order of list elements, with assignment inside the listPerl - 列表元素的评估顺序,在列表内分配
【发布时间】:2021-01-29 15:46:23
【问题描述】:

根据 Perldoc(https://perldoc.perl.org/perlop#Comma-Operator):

逗号运算符

...

在列表上下文中,它只是列表参数分隔符,并将其两个参数都插入到列表中。这些参数也是从左到右计算的。

在下面的代码中,我认为$n 应该是1,但它变成了2。我错过了什么?

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

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

即使我改变了顺序:

my $v = 1;
# my $n = ( $v, $v = 2 )[0];
my $n = ( $v = 2, $v )[1];
say "n = $n";  # still 2
say "v = $v";  # 2

【问题讨论】:

  • my $n = ( "$v", $v = 2 )[0]; 的工作表明 $v 在您认为的情况下并未评估为其值。
  • @choroba 谢谢,我试过$v1+0,它也按预期工作。

标签: perl evaluation


【解决方案1】:

它确实会从左到右进行评估。

$ 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

问题是与$v 关联的标量放在堆栈上,而不是整数。你可以在这里看到发生了什么:

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

如果您更喜欢别名而不是引用:

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

【讨论】:

  • 哇...非常感谢您提供如此详细的信息。虽然我无法理解 B::Concise 的输出,但 Perl 中的第二个代码很容易理解。现在我知道里面发生了什么。但是,我仍然认为这种行为有些奇怪(?)。不是吗?
  • 你觉得下面的奇怪吗?:$v =0; f($v, $v);子 f { $_[0]++; $_[1]++ };打印 $v; # 打印 2
  • @gypark,不,我没有发现 $v 将标量 $v 放在堆栈上很奇怪。奇怪的是发布的代码。奇怪的代码做意想不到的事情并不奇怪。
  • @DaveMitchell 我不知道,因为我知道@_ 的元素是perldoc perlsub 参数的别名。 :) 也许我应该为我的问题阅读更多文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-08
  • 2021-07-07
  • 2023-03-29
  • 1970-01-01
相关资源
最近更新 更多