Search code examples
perlone-liner

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?


Solution

  • 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.