Forum Discussion

AZ89's avatar
AZ89
Copper Contributor
May 08, 2025
Solved

Help Needed with Using Thunks with SCAN

I'm extremely new to the world of Lambda functions and the concept of using Thunks to get around an Array of Array limitations of the SCAN function.

The problem I'm trying to solve requires tracking the value of 3 variables across multiple iterations of a formula where the output of one iteration will feed into the input of the next iteration. I build the below simplified formula as a proof of concept but its been giving me Calc# errors throughout.

=LET( thunk, LAMBDA(x ,LAMBDA(x)),

 mythunk, SCAN( thunk( CHOOSE ({1 ,2 ,3} ,0 ,0 ,0)),  sequence(1,3),  lambda(T hunk_state ,i, thunk(

let( state, Thunk_state(), X, Index(state,1),  Y, Index(state,2),  Z, Index(state,3),

                                          X1, X+1,  Y1, Y+2,  Z1 ,Z+3,  CHOOSE({1,2,3},X1,Y1,Z1))))),

        Result, Index(mythunk,1,3),

    Index(Result(),1))

The logic of the formula is that the initial Array {0,0,0} is wrapped in a thunk to be compatible with SCAN, where in the Lambda function within SCAN the Thunk is unwrapped, Arithmetic operation is performed and rewrapped in the Thunk for the next iteration. Finally the Final Thunk is extracted and then unwrapped before displaying only the value of the first variable in the cell.

Hope someone can take a look and point out any issues with the syntax or Logic.

  • You were pretty much there

    The changes I made were

    1. to remove the CHOOSE function to simplify the definition of arrays
    2. to avoid the (illegal) use of cell references as LET variables
    3. To simplify the evaluation of the result of the SCAN

    The main issue was the fact that cell addresses are not valid as names within LET.  I have also demonstrated the passing of functions as parameters and included a demonstration of the evaluation of arrays of thunks.  Your formula using REDUCE offers the most effective approach if you only require values returned from the final step.

     

4 Replies

  • djclements's avatar
    djclements
    Bronze Contributor

    As an alternative, you could use a CHOOSE-based lambda function to store/recall multiple variables at each iteration. For example:

    =LET(
       valλ, LAMBDA([value1],[value2],[value3],LAMBDA(index_num,CHOOSE(index_num,value1,value2,value3))),
       arrϑ, SCAN(valλ(),SEQUENCE(3),LAMBDA(prevλ,i,LET(
          x, prevλ(1)+1,
          y, prevλ(2)+2,
          z, prevλ(3)+3,
          valλ(x,y,z)))),
       INDEX(arrϑ,3,1)({1,2,3})
    )

    MAP can then be used to unravel the entire array of results:

    =LET(
       valλ, LAMBDA([value1],[value2],[value3],LAMBDA(index_num,CHOOSE(index_num,value1,value2,value3))),
       vals, SEQUENCE(1,3),
       arrϑ, SCAN(valλ(),SEQUENCE(5),LAMBDA(prevλ,i,valλ(prevλ(1)+1,prevλ(2)+2,prevλ(3)+3))),
       MAP(IF(vals,arrϑ),IFNA(vals,arrϑ),LAMBDA(ϑ,v,ϑ(v)))
    )

    vals is horizontal vector, {1,2,3}, representing the index_num for the 3 variables of valλ.

    arrϑ returns a vertical vector of valλ functions containing the results of each iteration.

    IF and IFNA are used to broadcast both vectors across each other, so MAP can then call each value from each iteration and return the entire 2D array of results.

    Cheers!

    • AZ89's avatar
      AZ89
      Copper Contributor

      This is interesting but I have a couple of questions,

      1. valλ, LAMBDA([value1],[value2],[value3] are the Value1, value2 etc the starting values here?
      2. What does the ({1,2,3}) do in INDEX(arrϑ,3,1)({1,2,3})
      • djclements's avatar
        djclements
        Bronze Contributor

        [Value1], [Value2] and [Value3] are the 3 parameters of the valλ function. I made them optional in this case as a shortcut for the initial_value, which was defined as valλ(), but could have defined as valλ(0,0,0) for clarity. The 3 omitted arguments of the initial_value are essentially treated as zeros when recalled in the first iteration of SCAN; e.g.: prevλ(1)+1 = 0+1 = 1, prevλ(2)+2 = 0+2 = 2, prevλ(3)+3 = 0+3 = 3, and when these three results are passed to valλ(1,2,3), it returns an uncalled lambda function, =LAMBDA(index_num,CHOOSE(index_num,1,2,3)).

        The result of arrϑ in this example is a vertical vector of uncalled lambda functions as follows:

        =LAMBDA(index_num,CHOOSE(index_num,1,2,3))
        =LAMBDA(index_num,CHOOSE(index_num,2,4,6))
        =LAMBDA(index_num,CHOOSE(index_num,3,6,9))

        INDEX(arrϑ,3,1) returns the last item from the vector of functions, so INDEX(arrϑ,3,1)({1,2,3}) is the same as saying =LAMBDA(index_num,CHOOSE(index_num,3,6,9))({1,2,3}), which is calling all three values from CHOOSE and returning {3,6,9}.

        For clarity, the first example shown in my previous post could be rewritten as follows:

        =LET(
           valλ, LAMBDA([value1],[value2],[value3],LAMBDA(index_num,CHOOSE(index_num,value1,value2,value3))),
           init, valλ(0,0,0),
           arrϑ, SCAN(init,SEQUENCE(3),LAMBDA(prevλ,i,LET(
              x, prevλ(1)+1,
              y, prevλ(2)+2,
              z, prevλ(3)+3,
              valλ(x,y,z)))),
           last_valλ, INDEX(arrϑ,3,1),
           HSTACK(last_valλ(1),last_valλ(2),last_valλ(3))
        )

        I hope that helps. If you have any more questions, just let me know. ;)

  • You were pretty much there

    The changes I made were

    1. to remove the CHOOSE function to simplify the definition of arrays
    2. to avoid the (illegal) use of cell references as LET variables
    3. To simplify the evaluation of the result of the SCAN

    The main issue was the fact that cell addresses are not valid as names within LET.  I have also demonstrated the passing of functions as parameters and included a demonstration of the evaluation of arrays of thunks.  Your formula using REDUCE offers the most effective approach if you only require values returned from the final step.

     

Resources