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

本文关键字:高级 函数 表达式 Lambda LINQ | 更新日期: 2023-09-27 18:06:39

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

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

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( 计算结果为 true 时,这个函数会返回循环,但我不明白 Loop(c,f( (f(n(( 是如何计算的 - 它们都被传递回 Loop 了吗?我尝试过在 Linqpad 中运行转储,但我只是不明白该位是如何运行的。

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

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

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

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

这很简单 - basicLoop是基于参数n返回n(对于 n>= 10(或使用递增参数调用自身的函数。因此,basicLoop(8)计算公式为:

  • basicLoop(8) 8 <10,因此调用basicLoop(8+1)以获得结果
  • basicLoop(9) 9 <10,因此调用basicLoop(9+1)以获得结果
  • basicLoop(10) 10 == 10,因此返回n即 10。
  • basicLoop(9)得到结果 10(来自 basicLoop(10) (并返回它
  • basicLoop(8)得到结果 10(来自 basicLoop(9) (并返回它

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

该条件的类型显然是(类似于n<10(-Func<int, bool>。所以现在我们有一些东西,它以Func<int,bool>作为参数并返回与我们的原始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函数,当您将条件传递给它时,它会返回相当于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 为真 - 调用具有相同条件并增加 n 的condLoop - condLoop(x => x < 5)(4 + 1)得到结果
  • condLoop(x => x < 5)(5) - 条件为 x => x < 5 , n = 5 所以当condition(5)被称为 x = 5 时,5 <5 是假的 - 返回 n 即 5
  • 返回condLoop(x => x < 5)(4) - 返回 5 作为condLoop(x => x < 5)(5)的结果

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

C# 的

匿名委托声明语法会导致混淆,因此我将用 F# 的函数类型语法重写它。

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

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

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

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

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

我可以想象Loop(c,f((f (n((部分最让你失望。调用 Loop 的结果是一个接受整数并返回整数的函数。n 是一个整数,f 是一个接受整数并返回整数的函数。就w而言,它将该整数增加 2。因此,给定 n 为 2,如第一个示例所示,您将 f(n(, 2 + 2 的结果作为新的 n 传递。只要 c(n( 解析为 true,你就会继续迭代,n 每次增加 2,直到大于或等于 10。

"我知道当 c(n( 计算结果为 true 时,此函数返回循环,但我不明白 Loop(c,f( (f(n(( 如何计算" - 该函数不返回Loop .该函数Loop,它返回一个Func<int, int>

因此,Loop是一个将两个函数作为输入并返回第三个函数的函数。它具有显示这一点的签名Func<Func<int , bool>, Func<int , int>, Func<int , int>>

现在,Loop = null第一行,以便 when Loop 实际上是在第二行定义的,它可以递归调用自己。

然后,Loop基本上是一个返回函数n => c(n) ? Loop(c, f)(f(n)) : n的函数。也就是说,Loop将返回一个函数,该函数在将来给定n时将返回Loop(c, f)(f(n)),当c(n) true时返回n,在false时返回;

然后w是参数 n => n < 10 & n => n + 2 时从 Loop 返回函数。

因此,将它们替换为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;
}

希望这种进展很容易看到。