razetime / ngn-k-tutorial

An ngn/k tutorial.
https://razetime.github.io/ngn-k-tutorial/
195 stars 22 forks source link

Scan examples give diffrent results in ngn/k #20

Closed yakubin closed 7 months ago

yakubin commented 8 months ago

In 05-adverbs.md there are a couple examples, which give different results in ngn/k:

{x+y*z}\[1; 1 2 3; 4 5 6; 7 8 9]
(1 2 3
 4 5 6
 7 8 9
 29 42 57)

In ngn/k that's:

 {x+y*z}\[1; 1 2 3; 4 5 6;7 8 9]
(1 2 3
 4 5 6)

To get a similar last line I need to pass 3 as the first argument:

 {x+y*z}\[3; 1 2 3; 4 5 6;7 8 9]
(1 2 3
 4 5 6
 7 8 9
 29 42 57)

It seems that the 4-argument (n+1-argument variant for functions taking n arguments) variant of scan in ngn/k interprets the first arguments as a recursion limit. If the first argument is n, then it prints an n+1-element array, where the first rows are arguments passed to scan except the initial one. Subsequent rows are calculated recursively by using previous rows as arguments to the function passed to scan.

I think it would also be worthwhile to mention the variant of scan that doesn't use the extra first argument:

 {x+y*z}\[1 2 3; 4 5 6;7 8 9]
(29 30 31
 69 70 71
 123 124 125)

This one seems to interpret the first argument as an array of initial values. The output then has the same number of columns as there are elements in the first argument. Subsequent arguments are interpreted as as arrays of values to be interpreted as each argument. x always holds the accumulator. For 3-argument functions then the second argument to scan holds values of y, and the third argument holds values of z. The second and third arguments need to have equal lengths. The number of rows in output is equal to the length of second and third arguments to scan.

I think most people who are aware of scan from functional languages expect the second variant. So far the tutorial mentioning only the variant used for building up sequences recursively seems a bit confusing (as well as the fact that trying it out in REPL gives different results).

The same applies to fold:

 {x+y*z}/[1;1 2 3; 4 5 6;7 8 9]
4 5 6
 {x+y*z}/[3;1 2 3; 4 5 6;7 8 9]
29 42 57
 {x+y*z}/[1 2 3; 4 5 6;7 8 9]
123 124 125
yakubin commented 8 months ago

Succinct demonstration of the difference:

Recursive (a short Fibonacci sequence implementation):

 +\[10;0;1]
0 1 1 2 3 5 8 13 21 34 55

Traditional (just 1 added to 0, one number, because the second argument is of length 1):

 +\[0;1]
1
razetime commented 8 months ago

I think some of these are addressed in #19. I will look into adding the other examples as well.

razetime commented 7 months ago

I have added a new section to chapter 5 covering this correctly now.