为什么斐波那契的这个 Perl 单行有效?

Why is this Perl one-liner of Fibonacci working?

print$f+=$z=$f-$z,$/for--$z..8

或者,如果将 $z 替换为 $!,则可以执行以下操作。

print$f+=$!=$f-$!for--$!..8

但是为什么呢? $! 是错误的 perlval,不是吗?

这是一种混淆。它之所以有效,是因为(来自 perlvar):

$ERRNO $!

When referenced, $! retrieves the current value of the C errno integer variable. If $! is assigned a numerical value, that value is stored in errno. When referenced as a string, $! yields the system error string corresponding to errno.

例如,

$ perl -M5.010 -e'
   $! = 2;
   say 0+$!;    # Treat as number
   say "$!";    # Treat as string

   $! = 3;
   say 0+$!;
   say "$!";
'
2
No such file or directory
3
No such process

如果我们放入一些 space 以使其更具可读性:

print $f += $z = $f - $z ,$/ for --$z .. 8

让我们将不同的部分提取到正常代码中。首先,使用常规 for 循环。

for (--$z .. 8) {
     print $f += $z = $f - $z ,$/
}

前缀--auto-decrement operator将取未初始化的变量$z,将其强制转换为0,减去1,然后return-1。这与将 -1 分配给 $z.

基本相同

我们从 -1 循环到 8。这些数字是无关紧要的,因为它们被分配给 $_,它从未被使用过。基本上我们只是循环 10 个步骤。此步骤中使用的变量无关紧要。它可以是任何可以赋值的变量,这就是为什么 $! -- OS error variable 也能工作。

我们可以将语句简化为

$z = -1;
for (0 .. 9)

打印语句主要打印两件事:

$f += $z = $f - $z

$/

两个用逗号分隔的语句,,表示它是一个列表。预定义变量$/input record separator;默认情况下它是换行符(OS 依赖)。 print 接受一个参数列表,这就是它的工作方式。 print 自动换行与 say 相同。所以我们可以交换它并将代码简化为:

say $f += $z = $f - $z

让我们解开那个作业。它基本上是两个语句,从右到左按顺序完成,以打印结尾:

$z = $f - $z
$f += $z
say $f

如果我们把它放在一起,我们得到:

$z = -1;
for (0 .. 9) {       # loop 10 times
    $z = $f - $z;    # calculate increment
    $f += $z;        # increment value
    say $f;          # print value
}

它是这样工作的:

我们知道 $z 最初是 -1 来自 for 循环。由于 $f 从未使用过,因此它将是未定义的(在减法上下文中使用时将被转换为 0)。所以我们得到:

$z = $f - $z = undef - (-1) = 0 + 1 = 1
$f += $z = 1 => $f = $f + 1 => $f = 1

因此第一个循环迭代打印 1。下一个回合

$z = $f - $z = 1 - 1 = 0
$f += $z = 0 => $f = $f + 0 = 1 + 0 = 1

下一张也是1。下一回合

$z = $f - $z = 1 - 0 = 1
$f += $z = 1 => $f = $f + 1 = 1 + 1 = 2

以此类推