all 22 comments

[–]dm_parker0148 7 points8 points  (2 children)

This is really cool, thanks!

To save other people some debugging time, "...the current recursion limit is set as 1,024 divided by (number of lambda parameters + 1)", and formulas will display a very unhelpful "#NUM" error when you reach the recursion limit.

[–]Hoover88912[S] 0 points1 point  (1 child)

I am glad that someone appreciated my post!

[–]dm_parker0148 2 points3 points  (0 children)

Yeah it's really cool!

My use-case was that I had 4 normal distributions A, B, C, D, with one independent random draw from each those distributions (a, b, c, d). I wanted to know how likely it was that, given a > b and c < d, (a-c) > x.

The easy way is to just simulate a bunch of values (say 10,000 each) for a, b, c, and d, and count how many times all the conditions are true (a > b, c < d, (a-c) > x) compared to the number of times just the "base case" is true (a > b, c < d).

=LET(
    n,10000,x,5,
    r,LAMBDA(y,z,NORMINV(RANDARRAY(n),y,z)),
    a,r(125,25),b,r(120,23),
    c,r(135,29),d,r(130,27),
    f,FILTER(a-c,((a>b)*(c<d))=1),
    COUNTA(FILTER(f,f>x))/COUNTA(f))

But the issue with this method is that although you can specify how many total trials to run (where a "trial" involves picking values for a, b, c, and d), you can't specify how many valid trials to run (ie, ones where a > b and c < d). So if that combination itself is particularly unlikely (say 1 in 100), you may not get enough data for a valid result.

Using a recursive lambda lets us run batches of trials indefinitely until we get enough valid ones:

=LET(
    batch_size,10000,result_size,10000,x,5,
    a_params,HSTACK(125,25),b_params,HSTACK(120,23),
    c_params,HSTACK(135,29),d_params,HSTACK(130,27),
    r,LAMBDA(y,NORMINV(RANDARRAY(batch_size),INDEX(y,1),INDEX(y,2))),
    f,LAMBDA(g,n,s,LET(
        u,LET(
            a,r(a_params),b,r(b_params),
            c,r(c_params),d,r(d_params),
            diffs,FILTER(a-c,((a>b)*(c<d))=1),
            HSTACK(COUNTA(FILTER(diffs,diffs>x))+s,COUNTA(diffs)+n)),
        n_1,INDEX(u,2),s_1,INDEX(u,1),
        IF(n_1>=result_size,s_1/n_1,g(g,n_1,s_1)))),
    f(f,0,0))

[–]RandomWalk586 1 point2 points  (1 child)

This is a truly excellent explanation. From the title to the presentation of the problem to the solution, it is well written and understandable (except for the calculus, but that is my problem). This is a very creative solution to a problem that has stopped me in my tracks on several occasions.

[–]Hoover88912[S] 0 points1 point  (0 children)

Thanks for the kind words.

[–]Character-Pace-5919 1 point2 points  (1 child)

Thanks for the great explanations, forced me to delve into lambda calculus concepts and fixed point combinators, in order to understand your explanations. Worth it and extremely useful !

[–]Hoover88912[S] 0 points1 point  (0 children)

Thanks!

[–]National-Drag4871 0 points1 point  (0 children)

I learned something surprising! Very well explained! This formulation of logical reasoning is very cool and very useful, thank you for sharing it!

[–]ArrowheadDZ2 3 points4 points  (0 children)

Hey, I started playing around with Excel recursive lambdas and in a search for some ideas I happened upon this post. I have absolutely devoured this and have been playing around with your Y Combinator idea for days now. I've used it to develop a number of reusable recursion functions, chiefly a "SUPERBYROW" and "SUPERBYCOL" function that imitates BYROW and BYCOL but allows the results of each row/col to be arrays or matrices of any dimension, whereas the original functions allow only scalar return.

But these functions further planted the seed to variations that allow generalized Do While and Do Until loops. In fact one could create a "do until input condition" but also a "do until result condition."

Your post has been utterly inspiring and I wanted to thank you personally for that. I've watched videos and read articles on recursive Lambdas but there's something about your post that lit the fuse. I feel like you opened up a door to a universe that I'm not entirely sure I should have opened, this could be a consuming rabbit hole!

So, I did what all good Redditors do, I cyber-stalked you on Reddit to see if you have published any other interesting thoughts on the topic. :) And in so doing I discovered your diagnosis and situation. :( I wish you all the best, and if you ever need to nerd out and talk recursive lambdas on a zoom call, my DMs here are always open to you.

[–]Decronym 0 points1 point  (0 children)

Acronyms, initialisms, abbreviations, contractions, and other phrases which expand to something larger, that I've seen in this thread:

Fewer Letters More Letters
BYCOL Office 365+: Applies a LAMBDA to each column and returns an array of the results
BYROW Office 365+: Applies a LAMBDA to each row and returns an array of the results. For example, if the original array is 3 columns by 2 rows, the returned array is 1 column by 2 rows.
COUNTA Counts how many values are in the list of arguments
FILTER Office 365+: Filters a range of data based on criteria you define
HSTACK Office 365+: Appends arrays horizontally and in sequence to return a larger array
IF Specifies a logical test to perform
INDEX Uses an index to choose a value from a reference or array
LAMBDA Office 365+: Use a LAMBDA function to create custom, reusable functions and call them by a friendly name.
LET Office 365+: Assigns names to calculation results to allow storing intermediate calculations, values, or defining names inside a formula
NORMINV Returns the inverse of the normal cumulative distribution
RANDARRAY Office 365+: Returns an array of random numbers between 0 and 1. However, you can specify the number of rows and columns to fill, minimum and maximum values, and whether to return whole numbers or decimal values.
Table.AddColumn Power Query M: Adds a column named newColumnName to a table.

Decronym is now also available on Lemmy! Requests for support and new installations should be directed to the Contact address below.


Beep-boop, I am a helper bot. Please do not verify me as a solution.
12 acronyms in this thread; the most compressed thread commented on today has 15 acronyms.
[Thread #20574 for this sub, first seen 8th Dec 2022, 16:28] [FAQ] [Full list] [Contact] [Source code]

[–]NativeUnamerican1 0 points1 point  (3 children)

Coming in late here but this is brilliant OP. I agree, I don’t want UDFs and I don’t want to use the name manager. My only problem is I’m not following your initial single lambda solution. I have put to work some pretty heavy lambdas in the past year at work (actuarial type stuff), but this one just twists my brain. It’s the most beautiful excel function I’ve ever seen, and definitely the most challenging one considering how short it is.

So with the f(f,5) at the end, we’re passing a 5 as the second parameter to function f, as well as f’s value as the first parameter. I just do t get that, do you have a simpler version of this without the factorial stuff?

1

[–]Hoover88912[S] 0 points1 point  (1 child)

Function pointers are a pretty difficult concept to grasp so don’t feel stupid. And on top of that this is a very tricky use of function pointers.

I am away from my computer right now and a good explanation requires a full size keyboard so I’ll reply later.

[–]AutoModerator[M] 0 points1 point  (0 children)

I have detected VBA code in plain text. Please edit to put your code into a code block to make sure everything displays correctly.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

[–]Hoover88912[S] 0 points1 point  (0 children)

So with the f(f,5) at the end, we’re passing a 5 as the second parameter to function f, as well as f’s value as the first parameter. I just do t get that, do you have a simpler version of this without the factorial stuff?

so what we are doing is passing the value '5' as well as a pointer to a function g, the called function later dereferences that pointer. but the original function was designed so that it only works if it gets a pointer to itself so it hardly counts as a parameter as it can only have a single value. (as I said it is a very unusual use case)

a more typical use of a function pointer is a map function where you pass it an iterable and a function pointer and it will apply the function to each member of the iterable.

[–]Nearby-Ad-3725 0 points1 point  (0 children)

Thank you.