与 C# 中的 Lambda/LINQ 表达式混淆的高级函数

High Level Function Confusion with Lambda/LINQ expressions in C#

不确定如何描述这个问题,所以标题可能有误。

正在阅读一些代码示例并对以下 return 函数感到困惑:

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 );

我知道当 c(n) 的计算结果为真时,这个函数 return 循环,但我不明白 Loop(c,f) (f(n)) 是如何计算的 - 是他们都被传回循环?我已经在 Linqpad 中尝试了 运行 转储,但我只是不明白那位是如何 运行。

任何帮助将不胜感激,明白这可能是一个愚蠢的问题!

C#的匿名委托声明语法容易混淆,所以我打算用F#的函数类型语法重写。

(int -> bool) -> (int -> int) -> (int -> int)

所以这是一个有两个函数的函数,return是一个函数。它接受的第一个函数是一个谓词,第二个可能是一个映射,最后它 return 是一个接受 int 到 return int 的函数。

Loop = (c , f ) => n => c(n) ? Loop(c , f ) ( f (n)): n;

上面签名的实现。正如预期的那样,它有两个参数,即 c 和 f。我们希望它是 return 一个函数,这里是:

n => c(n) ? Loop(c,f) (f (n)) : n;

我可以想象 Loop(c,f) (f (n)) 部分最让您失望。调用 Loop 的结果是一个接受整数和 returns 整数的函数。 n 是一个整数,f 是一个接受整数和 returns 整数的函数。在 w 的术语中,它将该整数递增 2。因此,给定 n 为 2,如在第一个示例中一样,您将传递 f(n) 的结果 2 + 2 作为新的 n。只要c(n)为真,就继续迭代,n每次递增2,直到大于等于10。

尝试理解它的一种方法是从小处着手:基本循环 1-10 和 +1 增量。

 Func<int,int> basicLoop = null;
 basicLoop = n => n < 10 ? basicLoop(n+1) : n;

这很简单 - basicLoop 是基于参数 n returns n(对于 n >= 10)或使用递增参数调用自身的函数。所以 basicLoop(8) 计算为:

  • basicLoop(8) 8 < 10,所以调用 basicLoop(8+1) 得到结果
  • basicLoop(9) 9 < 10,所以调用 basicLoop(9+1) 得到结果
  • basicLoop(10) 10 == 10,所以 returns n 即 10.
  • basicLoop(9) 得到结果 10(来自 basicLoop(10))并且 returns 它
  • basicLoop(8) 得到结果 10(来自 basicLoop(9))并且 returns 它

现在我们要将条件作为参数传递给循环。这意味着我们的 "loop" Func 将需要在每次迭代中传递该条件:

该条件的类型显然是(类似于 n<10)- Func<int, bool>。所以现在我们有了一些东西,它以 Func<int,bool> 作为参数并且 returns 与我们原来的 basicLoop 相同的值。因此它将是 Func 个参数和一个结果:

Func<Func<int, bool>, Func<int,int>> condLoop = null;

condLoop 是一个参数的函数 - 所以在定义时我们采用参数:condLoop = (condition) => ...

我们需要替换原来basicLoop中的条件:n => n < 10 ? ...变成n => condition(n) ? ...

最后一部分是替换 basicLoop(n+1) - 我们有 condLoop 函数,当您将条件传递给它时,returns 相当于 basicLoop。幸运的是,我们的条件在迭代之间不会改变,我们已经有了它——condLoop(condition) 等价于 basicLoop。把它们放在一起:

condLoop = (condition) =>
   n => condition(n) ? 
      condLoop(condition) (n + 1) :
      n; 

通过调用进行跟踪 condLoop(x => x < 5)(4)

  • condLoop(x => x < 5)(4) - 条件为 x => x < 5,n = 4 所以当调用 condition(4) 时 x = 4,4 < 5 为真 - 以相同条件调用 condLoop并增加 n - condLoop(x => x < 5)(4 + 1) 得到结果
  • condLoop(x => x < 5)(5) - 条件是 x => x < 5, n = 5 所以当 condition(5) 被调用时 x = 5, 5 < 5 是 false - 返回 n 即 5
  • 返回 condLoop(x => x < 5)(4) - 作为 condLoop(x => x < 5)(5)
  • 的结果返回 5

使用类似的逻辑添加函数来增加值 - 在每次迭代中,现在您需要传递 conditionincrement 函数(cf 在原始 post).

"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" - 该函数没有 return Loop。函数 Loop 而它 return 是 Func<int, int>.

因此,Loop 是一个将两个函数作为输入的函数,return 是第三个函数。它的签名 Func<Func<int , bool>, Func<int , int>, Func<int , int>> 表明了这一点。

现在,Loop = null 在第一行,这样当 Loop 实际上在第二行定义时,它可以递归地调用自己。

那么,Loop基本上就是return函数n => c(n) ? Loop(c, f)(f(n)) : n的一个函数。也就是说,Loop 将 return 一个函数,如果将来给定一个 n,当 c(n) 是 [=25= 时,它将 return Loop(c, f)(f(n)) ] 和 n 当它是 false;

w 是来自 Loop 的 return 函数,当参数为 n => n < 10 & n => n + 2.

因此,将 Loop 中的那些代入,您可以这样定义 w

Func<int, int> w = null;
w = n => n < 10 ? w(n + 2) : n;

现在可以改写为:

int w(int n)
{
    while (n < 10)
    {
        n += 2;
    }
    return n;
}

希望这种进步是显而易见的。