Unsure how to describe this question so the title might be wrong.
Am reading over some code examples and am confused over the following return function:
Func<Func<int , bool>, Func<int , int>, Func<int , int>> Loop = null ;
Loop = (c , f ) => n => c(n) ? Loop(c , f ) ( f (n)): n;
Func<int , int> w = Loop(n => n < 10 , n => n + 2);
var r = w(2);
var s = w(3);
Console . WriteLine ("{0} {1}" , r , s );
I understand that this function is returning the Loop when c(n) evaluates to true, but I don't understand how Loop(c,f) (f(n)) evaluates - are they both being passed back into Loop? I have tried running dumps in Linqpad and I just don't get how that bit is running.
Any help would be appreciated, understand this is probably a dumb question!
"I understand that this function is returning the Loop when c(n) evaluates to true, but I don't understand how Loop(c,f) (f(n)) evaluates" - the function doesn't return
Loop
. The function isLoop
and it returns aFunc<int, int>
.So,
Loop
is a function that takes two functions as input and returns a third function. It has the signatureFunc<Func<int , bool>, Func<int , int>, Func<int , int>>
which show this.Now,
Loop = null
in the first line so that the whenLoop
is actually defined on the second line it can recursively call itself.Then,
Loop
is basically a function that returns the functionn => c(n) ? Loop(c, f)(f(n)) : n
. That isLoop
will return a function that, given ann
in the future, will returnLoop(c, f)(f(n))
whenc(n)
istrue
andn
when it isfalse
;w
is then the return function fromLoop
when with the parametersn => n < 10
&n => n + 2
.So, substituting those in to
Loop
you could definew
like this:Now that can be re-written as this:
And hopefully that progression is easy to see.
One way to try to understand it is start small: have basic loop 1-10 with +1 increment.
That's pretty simple -
basicLoop
is function that based on parametern
returnsn
(for n >= 10) or calls itself with incremented parameter. SobasicLoop(8)
is computed as:basicLoop(8)
8 < 10, so callsbasicLoop(8+1)
to get resultbasicLoop(9)
9 < 10, so callsbasicLoop(9+1)
to get resultbasicLoop(10)
10 == 10, so returnsn
which is 10.basicLoop(9)
got result 10 (frombasicLoop(10)
) and returns itbasicLoop(8)
got result 10 (frombasicLoop(9)
)and returns itNow we want to pass condition as parameter to the loop. That means our "loop"
Func
will need to pass that condition around on every iteration:Type of that condition is clearly (similar to
n<10
) -Func<int, bool>
. So now we have something that takesFunc<int,bool>
as argument and returns same value as our originalbasicLoop
. Hence it will beFunc
of one argument and one result:condLoop
is function of one argument - so when defining we take argument:condLoop = (condition) => ...
.We need to replace condition in our original
basicLoop
:n => n < 10 ? ...
becomesn => condition(n) ? ...
.The last part is replacing
basicLoop(n+1)
- we havecondLoop
function that returns equivalent ofbasicLoop
when you pass condition to it. Fortunately our condition does not change between iterations and we already have it -condLoop(condition)
is equivalent ofbasicLoop
. Getting it all together:Tracing through calls
condLoop(x => x < 5)(4)
condLoop(x => x < 5)(4)
- condition isx => x < 5
, n = 4 so whencondition(4)
is called x = 4, 4 < 5 is true - callingcondLoop
with same condition and increased n -condLoop(x => x < 5)(4 + 1)
to get resultcondLoop(x => x < 5)(5)
- condition isx => x < 5
, n = 5 so whencondition(5)
is called x = 5, 5 < 5 is false - returningn
which is 5condLoop(x => x < 5)(4)
- returning 5 as result ofcondLoop(x => x < 5)(5)
With similar logic adding function that increments value - on every iteration now you need to pass
condition
andincrement
function (c
andf
in original post).C#'s anonymous delegate declaration syntax leads to confusion, so I'm going to rewrite it in F#'s function type syntax.
So this is a function that takes two functions and returns a function. The first function it accepts is a predicate, the second is likely a map, and finally it returns a function that accepts an int to return an int.
The implementation of the above signature. It takes two parameters, as expected, as c and f. We expect it to return a function, and here it is:
I can imagine the Loop(c,f) (f (n)) part is throwing you the most. The result of calling Loop is a function that accepts an integer and returns an integer. n is an integer, and f is a function that takes an integer and returns an integer. In the terms of
w
it increments that integer by 2. So, given an n of 2, as in the first example, you would pass the result of f(n), 2 + 2, as a new n. So long as c(n) resolves true, you will continue to iterate, with n increasing by 2 each time until greater than or equal to 10.