Search code examples
sequencerakupascals-triangle

Question on the array generating sequence in Raku


I've come across this code at rosettacode

my @pascal = [1], { [0, |$_ Z+ |$_, 0] } ... Inf;
.say for @pascal[^4];
# ==>
# [1]
# [1 1]
# [1 2 1]
# [1 3 3 1]

Inside the explicit generator block, I know how the individual operators like the list flattening | and the zip operator Z+ work but I have difficulty comprehending how they cooperate to generate the next array. Can somebody explain in detail how it works? Thank you.

Note: The code is slightly rearranged for brevity, i.e. it's superficially different than the one in Rosetta.


Solution

  • So let's take a look at what's going on.

    First up the Sequence generator ... this takes a list of starting values, a code block itself and and end point.

    It uses the starting values to generate each next item in the list so lets start with @pascal[0] that's simple enough : [1]

    For @pascal[1] we call the code block with our value in @pascal[0] like so :

    sub ( $array ) { 
      [0, |$array Z+ |$array, 0] 
    }
    

    You'll note I've made it into a sub, this is just so I can explain things easier. In the anonymous code block $_ is the incoming data and I've called it $array. So what code to we run when $array == [1]?

    [0, |[1] Z+ |[1], 0] => [1,1] 
    

    So here's a question what happens if we don't use the | ?

    [0, [1] Z+ [1], 0] => [1,1] 
    

    It's the same! So whys it there? Well what happens if we set $array == [3]?

    [0, [3] Z+ [3], 0] => [1,1] 
    

    Weird? No, because the Z+ transforms like this :

    [0, [3] Z+ [3], 0] => [0 + [3], [3] + 0] => [1,1] 
    

    Z makes a new list by zipping the elements with the given operator + between them. + is doing a numerical computation and the Numerical representation of an Array is the number of elements in it, in this case 1 both times.

    And this is where the | slip operator comes in, it slips the array it's given open merging into the list context it's in. So lets go back to @pascal[1]

    [0, |[1] Z+ |[1], 0] => [0, 1 Z+ 1, 0] => [0 + 1, 1 + 0] => [1,1] 
    

    Ok.. So @pascal[2] is calling the same block but now passing in [1,1]

    [0, |[1, 1] Z+ |[1, 1], 0] => [0, 1, 1 Z+ 1, 1, 0] => [0 + 1, 1 + 1, 1 + 0] => [1,2,1] 
    

    And so on into Infinity!

    I hope that's helped to explain what's going on?