Forum Discussion
Lambda Example: Generate Fibonacci series
Just an update regarding the earlier comment to include a couple more efficient fibonacci methods using SCAN. One way is to process the arrays as thunks,
=MAP(
SCAN(
LAMBDA({0, 1}),
SEQUENCE(1000),
LAMBDA(thunk, i, LET(fib, MMULT(thunk(), {1, 1; 1, 0}), LAMBDA(fib)))
),
LAMBDA(thunk, INDEX(thunk(), 1))
)another is to define a list function as a wrapper for the arrays,
=LET(
list, LAMBDA(array, LAMBDA(i, INDEX(array, i))),
BYROW(
1,
SCAN(
list({0, 1}),
SEQUENCE(1000),
LAMBDA(a, i, LET(fib, MMULT(a(0), {1, 1; 1, 0}), list(fib)))
)
)
)Both are very fast when compared to REDUCE/VSTACK, something that makes a big difference is to define a variable like 'fib' to store the intermediate results as a static array.
lori_mthis is awesome. I took a bit of time to study this solution as THUNKs in general have been a concept I have brushed with but don't feel I have fully grounded functional grasp of. So I keep a cheat cheat of various useful functions and links and notes and such and wrote this up. I hope a) to get feedback that my personal descriptions of what is happening is correct and maybe b) some help to others that may be struggling with this concept as well.
so concept of a THUNK function is basically using LAMBDA as a pointer to a value, array or function
here are 2 concepts using this for Fibonacci Sequence creation and 'bypassing' array of arrays issue in a faster way (from lori_m )
| =MAP(SCAN(
| LAMBDA({0, 1}),
| SEQUENCE(1000),
| LAMBDA(thunk, i, LET(fib, MMULT(thunk(), {1, 1; 1, 0}), LAMBDA(fib)))
| ),
| LAMBDA(thunk, INDEX(thunk(), 1)))
So the scan starts with and passes a LAMBDA pointer each loop and each case is a 1x2 array (new val, old val)
Then the MAP is passed all those LAMBDAs/pointers and returns a single value for each (index) and hence a valid output
| =LET(list, LAMBDA(array, LAMBDA(i, INDEX(array, i))),
| BYROW(
| 1,
| SCAN(
| list({0, 1}),
| SEQUENCE(1000),
| LAMBDA(a, i, LET(fib, MMULT(a(0), {1, 1; 1, 0}), list(fib)))
| )))
In this case "list" is defined as LAMBDA( x, LAMBDA( y, ...)) which allows one to call list(a) and return a LAMBDA/pointer
and it is this LAMBDA/pointer that is sent initially and each loop. The BYROW then applies the BYROW across all those LAMBDAs and since that LAMBDA in this case is a function INDEX(..., x) it will return the 1st index of each previously stored array
just as 'proof' of this concept the following is equivalent:
| =LET(list, LAMBDA(array, LAMBDA(i, INDEX(array, i))),
| MAP(
| SCAN(
| LAMBDA(i, INDEX({0,1},i)),
| SEQUENCE(1000),
| LAMBDA(a,i, LET(fib, MMULT(a(0), {1,1;1,0}), list(fib)))
| ),
| LAMBDA(x, x(1))))
so instead of using the built in BYROW helper this version uses the MAP and calls each of the stored LAMBDAs with the argument of 1
and finally as a question. If I'm not mistaken you are saying that this, which is a different work around to the array of arrays limitation of LAMBDAs, is significantly faster than the DROP(REDUCE(...LAMBDA(.. STACK())) approach. and can you elaborate on this as I see PeterBartholomew1 making many of these THUNK based solution which I find fascinating and neat in how they create this level of abstractness and even black box solution conceptually but it seems those solutions tended to fair poorer in terms of speeds.
- PeterBartholomew1Aug 18, 2023Silver Contributor
lori_m wrote:If lambda arguments are evaluated using LET in advance, performance can improve dramatically.
That is something I have never managed to achieve! Since the Thunks are subject to Lazy evaluation, I have always finished by re-evaluating the entire array each time I wish to access an element of the array which is very expensive computationally. Actually performing the array calculation and then gathering the result as a Lambda array would appear to offer a great route forward.
I do not appear to have got savings from my attempt to replicate your approach as yet and seem to require further guidance. The tests I have tried with the first formula are based upon 4 rows of 1000 random values, which I sort or accumulate row by row. In each case my attempt to replicate your 1st formula performs much the same as REDUCE/HSTACK rather than accumulating or sorting the 2D array as a single hit.
The second formula looks promising applied to the Fibonacci series but I have yet to extract the ideas to apply them to other problems.
- lori_mAug 18, 2023Iron Contributor
Your description looks pretty much spot on, I guess you are coming from vba or other imperative programming background so are converting to loops. In the functional world one only needs functions and values - even arrays can be treated as functions like in the 'list' function. In your MAP equivalent one could pass a second argument of column indices {1,2} to return both columns. BYROW was used for the single column case only because it removed an extra lambda.
A key point I hadn't fully appreciated before was that wrapping LAMBDA around an expression causes re-evaluation for each function call, this can cause a big slowdown for example within SCAN. If lambda arguments are evaluated using LET in advance, performance can improve dramatically.