简体   繁体   中英

Why is this Perl one-liner of Fibonacci working?

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

Alternatively, if you replace $z to $!, you can do the below.

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

But why? $! is error perlval, isn't it?

If we put some space in to make it more readable:

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

Let's extract the different parts into normal code. First, use a regular for loop.

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

The prefix -- auto-decrement operator will take the uninitialized variable $z , cast it to 0, subtract 1, and return -1 . This is basically the same as assigning -1 to $z .

We loop from -1 to 8. These numbers are irrelevant, since they are assigned to $_ , which is never used. Basically we just loop 10 steps. The variable used in this step is irrelevant. It can be any variable that can be assigned to, which is why $! -- the OS error variable works as well.

We can simplify the statement to

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

The print statement basically prints two things:

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

and

$/

Two statements separated by a comma , , meaning it is a list. The predefined variable $/ is the input record separator ; it is a newline by default (OS dependent). print takes a list of arguments, which is how this is meant to work. print with automatic newline is the same as say . So we can exchange that and simplify the code to:

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

Let's untangle that assignment. It is basically two statements, done in sequence from right to left, ending with a print:

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

If we put it together, we get:

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

It works like this:

We know that $z initially is -1 from the for loop. And since $f is never used, it will be undefined (which will be cast to 0 when used in subtraction context). So we get:

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

So the first loop iteration prints 1. Next turn

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

Next print is 1 too. Next turn

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

And so on.

This is an obfuscation. It works because (from 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.

For example,

$ 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM