首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C# lambda - curry用例

C# lambda - curry用例
EN

Stack Overflow用户
提问于 2009-02-06 12:16:15
回答 6查看 6.5K关注 0票数 12

我读了This article,我发现它很有趣。

对于那些不想阅读整篇文章的人来说,总结一下。作者实现了一个名为Curry的高阶函数,如下所示(我在没有内部类的情况下进行了重构):

代码语言:javascript
复制
 public static Func<T1, Func<T2, TResult>> 
             Curry<T1, T2, TResult>(this Func<T1, T2, TResult> fn)
 {
     Func<Func<T1, T2, TResult>, Func<T1, Func<T2, TResult>>> curry = 
     f => x => y => f(x, y);
     return curry(fn);
 }

这给了我们这样的能力,像F(x,y)这样的表达式。

代码语言:javascript
复制
Func<int, int, int> add = (x, y) => x + y;

并以F.Curry()(x)(y)方式调用;

这部分我理解了,我发现它很酷,很极客。我没能理解的是这种方法的实际用法。何时何地需要这项技术,能从中获得什么?

提前谢谢。

编辑:在最初的3个响应之后,我知道在某些情况下,当我们从create创建一个新函数时,一些参数不会被重新计算。我用C#做了这个小测试(请记住,我只对C#实现感兴趣,而不是一般的curry理论):

代码语言:javascript
复制
public static void Main(string[] args)
{
    Func<Int, Int, string> concat = (a, b) => a.ToString() + b.ToString();
    Func<Int, Func<Int, string>> concatCurry = concat.Curry();
    Func<Int, string> curryConcatWith100 = (a) => concatCurry(100)(a);

    Console.WriteLine(curryConcatWith100(509));
    Console.WriteLine(curryConcatWith100(609));
}

    public struct Int
    {
        public int Value {get; set;}

        public override string ToString()
        {
             return Value.ToString();
        }

        public static implicit operator Int(int value)
        {
            return new Int { Value = value };
        }
    }

在连续两次调用curryConcatWith100时,值为100的ToString()求值被调用了两次(每次调用一次),所以我在这里看不到求值的任何好处。我是不是漏掉了什么?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2009-02-06 12:24:04

首先考虑fn(x,y,z)比较容易。这可以通过使用fn(x,y)提供一个函数,该函数只接受一个参数z。任何需要单独使用x和y完成的操作都可以通过一个闭包来完成并存储,返回的函数持有该闭包。

现在,您使用不同的z值多次调用返回的函数,而不必重新计算所需的x和y部分。

编辑:

有效地说,有两个原因需要使用curry。

参数约简

正如卡梅隆所说,将一个接受2个参数的函数转换为只接受1个参数的函数。用一个参数调用这个curried函数的结果与用2个参数调用原始函数的结果相同。

对于C#中存在的Lambdas,它的价值是有限的,因为它们无论如何都可以提供这种效果。虽然您使用的是C# 2,但是您问题中的Curry函数具有更大的价值。

分段计算

另一个使用curry的原因是我之前说过的。当最后的参数被提供给curried函数时,允许复杂/昂贵的操作被分段和多次重用。

这种类型的currying在C#中是不可能的,它确实需要一种函数式语言来实现它的任何函数。

结论

您提到的通过库里减少参数在C# 2中很有用,但在C# 3中由于Lambdas而大大降低了价值。

票数 12
EN

Stack Overflow用户

发布于 2009-02-06 13:04:42

Currying用于将具有x参数的函数转换为具有y参数的函数,因此可以将其传递给需要具有y参数的函数的另一个函数。

例如,Enumerable.Select(this IEnumerable<T> source, Func<TSource, bool> selector)接受一个只有一个参数的函数。Math.Round(double, int)是一个有2个参数的函数。

您可以使用currying将Round函数“存储”为数据,然后将这个curried函数传递给Select,如下所示

代码语言:javascript
复制
Func<double, int, double> roundFunc = (n, p) => Math.Round(n, p);
Func<double, double> roundToTwoPlaces = roundFunc.Curry()(2);
var roundedResults = numberList.Select(roundToTwoPlaces);

这里的问题是还有匿名委托,这使得currying变得多余。事实上,匿名委托是一种讨好的形式。

代码语言:javascript
复制
Func<double, double> roundToTwoPlaces = n => Math.Round(n, 2);
var roundedResults = numberList.Select(roundToTwoPlaces);

或者甚至只是

代码语言:javascript
复制
var roundedResults = numberList.Select(n => Math.Round(n, 2));

鉴于某些函数式语言的语法,Currying是一种解决特定问题的方法。有了匿名委托和lambda运算符,.NET中的语法就简单多了。

票数 18
EN

Stack Overflow用户

发布于 2009-02-06 12:27:12

在某种意义上,curring是一种实现自动局部应用的技术。

更正式地说,currying是一种将函数转换为只接受一个参数的函数的技术。

反过来,当被调用时,该函数返回另一个接受一个且仅接受一个参数的函数。。。依此类推,直到“原始”函数能够被执行。

codingforums中的线程

我特别喜欢这篇page上的解释和篇幅。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/520083

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档