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

时间:2016-09-13 00:38:28

标签: c# linq lambda linqpad

不确定如何描述此问题,因此标题可能有误。

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

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中运行转储,我只是不知道那个位是如何运行的。

任何帮助都会受到赞赏,明白这可能是一个愚蠢的问题!

3 个答案:

答案 0 :(得分:4)

尝试理解它的一种方法是从小开始:基本循环1-10,增量为+1。

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

非常简单 - basicLoop是基于参数n的函数返回n(对于n&gt; = 10)或使用递增参数调用自身。所以basicLoop(8)计算为:

  • basicLoop(8) 8&lt; 10,所以请basicLoop(8+1)来获取结果
  • basicLoop(9) 9&lt; 10,所以请basicLoop(9+1)来获取结果
  • basicLoop(10) 10 == 10,所以返回n即10。
  • basicLoop(9)得到结果10(来自basicLoop(10))并将其返回
  • basicLoop(8)得到结果10(来自basicLoop(9))并将其返回

现在我们要将条件作为参数传递给循环。这意味着我们的循环&#34; 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&lt; 5是真的 - 调用具有相同条件的condLoop并增加n - condLoop(x => x < 5)(4 + 1)以获得结果
  • condLoop(x => x < 5)(5) - 条件为x => x < 5,n = 5,因此当调用condition(5)时x = 5,5 <&lt; 1} 5是假的 - 返回n即5
  • 返回condLoop(x => x < 5)(4) - 因condLoop(x => x < 5)(5)
  • 而返回5

使用类似的逻辑添加函数增加值 - 在每次迭代时,您需要在原始帖子中传递conditionincrement函数(cf)。< / p>

答案 1 :(得分:1)

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)解析为真,你就会继续迭代,每次n增加2,直到大于或等于10。

答案 2 :(得分:1)

&#34;我知道当c(n)求值为true时,此函数返回循环,但我不明白循环(c,f)(f(n))如何评估&#34; - 该功能不返回Loop。函数 Loop,它返回Func<int, int>

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

现在,Loop = null在第一行,以便在第二行实际定义时Loop,它可以递归调用自身。

然后,Loop基本上是一个返回函数n => c(n) ? Loop(c, f)(f(n)) : n的函数。那是Loop将返回一个函数,如果将来nLoop(c, f)(f(n)) c(n) truen时会返回false它是w;

Loopn => n < 10的返回函数,当参数为n => n + 2&amp; 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; }

Column A (Error.Type) |
(Or any other text)   | Column B (Message)  
> 1                   | #REF!               |
> 1                   | #REF!               | total for error 1 is 2
> 2                   | #DIV/0!             |
> 2                   | "Hello World"       | total for error 2 is 2
> 3                   | "Foobar"            | 
> 3                   | "Something"         |
> 3                   | "Else"              | total for error 3 is 3

现在可以重写为:

2 + 2 + 3 = 7

希望这种进展很容易看出来。