【问题标题】:High Level Function Confusion with Lambda/LINQ expressions in C#C# 中 Lambda/LINQ 表达式的高级函数混淆
【发布时间】:2016-09-13 00:38:28
【问题描述】:

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

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

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,但我不明白 Loop(c,f) (f(n)) 的评估方式 - 它们是否都被传递回 Loop ?我曾尝试在 Linqpad 中运行转储,但我只是不明白该位是如何运行的。

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

【问题讨论】:

    标签: c# linq lambda linqpad


    【解决方案1】:

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

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

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

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

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

    该条件的类型显然是(类似于n&lt;10)-Func&lt;int, bool&gt;。所以现在我们有了一些将Func&lt;int,bool&gt; 作为参数并返回与我们原来的basicLoop 相同的值。因此它将是一个参数和一个结果的Func

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

    condLoop 是一个参数的函数 - 所以在定义时我们接受参数:condLoop = (condition) =&gt; ...

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

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

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

    跟踪调用condLoop(x =&gt; x &lt; 5)(4)

    • condLoop(x =&gt; x &lt; 5)(4) - 条件是x =&gt; x &lt; 5,n = 4 所以当condition(4) 被调用时 x = 4,4 condLoop 并增加 n - condLoop(x =&gt; x &lt; 5)(4 + 1) 以获得结果
    • condLoop(x =&gt; x &lt; 5)(5) - 条件是 x =&gt; x &lt; 5, n = 5 所以当 condition(5) 被调用时 x = 5, 5 n 这是 5
    • 返回condLoop(x =&gt; x &lt; 5)(4) - 作为condLoop(x =&gt; x &lt; 5)(5) 的结果返回5

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

    【讨论】:

    • 啊!所以语法 Loop(c, f)(f(n)) 调用 (f(n)) 作为调用 Loop(c,f) 的条件?我只是在为我认为的语法如何运作而苦苦挣扎。
    • 我不会将其称为“召回循环的条件”——也许您只是将“条件”用作“下一次迭代的值”...Loop(c, f) 返回一个参数的函数,类似于basicLoop 一个。 f(n) 只是调用 f 函数 .... 结合 - Loop(c,f) (f(n)) 是调用 Loop(c, f)f(n)... 的结果或换句话说 - n 的下一次迭代应该是什么循环...
    • 您可以尝试传递记录每个调用的函数,例如条件n =&gt; {Console.WriteLine("Condition {0}", n);return n &lt; 10;} 并盯着输出一段时间...
    • 谢谢,我不明白 f(n)(n) 的语法 - 我很愚蠢地忽略了。不过我确实理解迭代,谢谢你的帮助!
    【解决方案2】:

    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。

    【讨论】:

    • 这真的很有帮助,但我想我只是在语法上遗漏了一些东西。使用 Loop (c,f) (f(n)),我不明白 Loop 是如何停止的 - 在调用 Loop(c,f) 以获得新值之前,(f(n)) 是否应该解决已经改变了吗?
    • f(n) 只是 int 值...在每次迭代中,它都会增加(对于 f = x =&gt;x + 2,如您的示例),如 3、5、7、9、11,...最终条件将为 false 以触发对 ?: 的最后一部分的评估,该部分通过返回当前值来停止递归。
    • 函数fw 的条件下接受一个数字并将其加2。 w 中的函数c 如果输入的数字小于10,则返回true。Loop(c,f) 返回n =&gt; c(n) ? Loop(c,f) (f (n)) : n; 你可以分步想。输入的第一个值是 2. 2 n => c(n) ? Loop(c,f) (f (n)) : n;。 Loop(c,f) (f (n)) == Loop(c,f) (4) == (n =&gt; c(n) ? Loop(c,f) (f(n)) : n) (4) 4 小于 10,因此分支转到 Loop(c,f) (f(4)),与 Loop(c,f) (6) 相同。你可以继续扩展它。
    【解决方案3】:

    “我知道当 c(n) 计算结果为真时,此函数正在返回循环,但我不明白 Loop(c,f) (f(n)) 是如何计算的” - 函数不返回Loop。函数Loop,它返回一个Func&lt;int, int&gt;

    所以,Loop 是一个将两个函数作为输入并返回第三个函数的函数。它的签名Func&lt;Func&lt;int , bool&gt;, Func&lt;int , int&gt;, Func&lt;int , int&gt;&gt; 显示了这一点。

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

    那么,Loop 基本上是一个返回函数n =&gt; c(n) ? Loop(c, f)(f(n)) : n 的函数。也就是说,Loop 将返回一个函数,给定未来的n,当c(n)truenfalse 时将返回Loop(c, f)(f(n))

    wLoop 使用参数n =&gt; n &lt; 10n =&gt; 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;
    }
    

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

    【讨论】:

      猜你喜欢
      • 2022-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多