C#Linq vs. Currying

Kai*_*Kai 15 c# linq currying

我正在尝试函数式编程及其各种概念.所有这些都非常有趣.我曾几次读过Currying以及它有什么优势.

但我不明白这一点.以下来源演示了咖喱概念的使用和linq的解决方案.实际上,我没有看到任何使用currying概念的建议.

那么,使用currying有什么好处?

static bool IsPrime(int value)
{
    int max = (value / 2) + 1;
    for (int i = 2; i < max; i++)
    {
        if ((value % i) == 0)
        {
            return false;
        }
    }
    return true;
}

static readonly Func<IEnumerable<int>, IEnumerable<int>> GetPrimes = 
        HigherOrder.GetFilter<int>().Curry()(IsPrime);

static void Main(string[] args)
{
    int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

    Console.Write("Primes:");
    //Curry
    foreach (int n in GetPrimes(numbers))
    {
        Console.Write(" {0}", n);
    }
    Console.WriteLine();

    //Linq
    foreach (int n in numbers.Where(p => IsPrime(p)))
    {
        Console.Write(" {0}", n);
    }

    Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)

这是HigherOrder过滤方法:

public static Func<Func<TSource, bool>, IEnumerable<TSource>, IEnumerable<TSource>> GetFilter<TSource>()
{
    return Filter<TSource>;
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 39

使用currying有什么好处?

首先,让我们澄清一些条款.人们使用"currying"来表示将两个参数的方法重新构造为一个参数的方法,该方法返回一个参数的方法,并且部分应用两个参数的方法以产生一个参数的方法.显然,这两项任务密切相关,因此存在混淆.在正式发言时,人们应该限制"讨论"以引用第一个定义,但是当非正式地说话时,使用是常见的.

所以,如果你有一个方法:

static int Add(int x, int y) { return x + y; }
Run Code Online (Sandbox Code Playgroud)

你可以这样称呼它:

int result = Add(2, 3); // 5
Run Code Online (Sandbox Code Playgroud)

你可以讨论这个Add方法:

static Func<int, int> MakeAdder(int x) { return y => Add(x, y); }
Run Code Online (Sandbox Code Playgroud)

现在:

Func<int, int> addTwo = MakeAdder(2);
int result = addTwo(3); // 5
Run Code Online (Sandbox Code Playgroud)

部分申请在非正式发言时有时也被称为"currying",因为它显然是相关的:

Func<int, int> addTwo = y=>Add(2,y);
int result = addTwo(3);
Run Code Online (Sandbox Code Playgroud)

您可以为您制作一台执行此过程的机器:

static Func<B, R> PartiallyApply<A, B, R>(Func<A, B, R> f, A a)
{
    return (B b)=>f(a, b);
}
...
Func<int, int> addTwo = PartiallyApply<int, int, int>(Add, 2);
int result = addTwo(3); // 5
Run Code Online (Sandbox Code Playgroud)

所以现在我们来问你的问题:

使用currying有什么好处?

这两种技术的优点在于它为您提供了处理方法的更大灵活性.

例如,假设您正在编写路径查找算法的实现.您可能已经有一个帮助方法,可以为您提供两点之间的近似距离:

static double ApproximateDistance(Point p1, Point p2) { ... }
Run Code Online (Sandbox Code Playgroud)

但是当您实际构建算法时,您经常想知道的是当前位置与固定终点之间的距离.算法需要的Func<Point, double>- 从位置到固定终点的距离是多少?你拥有的是什么Func<Point, Point, double>.你怎么把你所拥有的东西变成你需要的东西?部分应用; 您将固定终点部分应用为近似距离方法的第一个参数,并得到一个与您的路径查找算法需要消耗的函数匹配的函数:

Func<Point, double> distanceFinder = PartiallyApply<Point, Point, double>(ApproximateDistance, givenEndPoint);
Run Code Online (Sandbox Code Playgroud)

如果首先使用了ApproximateDistance方法:

static Func<Point, double> MakeApproximateDistanceFinder(Point p1) { ... }
Run Code Online (Sandbox Code Playgroud)

那你就不需要自己做部分应用了; 你只需MakeApproximateDistanceFinder用固定的终点来打电话就可以了.

Func<Point, double> distanceFinder = MakeApproximateDistanceFinder(givenEndPoint);
Run Code Online (Sandbox Code Playgroud)


Pre*_*gha 5

@Eric Lippert评论C#中Currying的优势是什么?(实现部分功能)指向此博客帖子:

Currying和Partial Function应用

在哪里我发现这个最适合我的解释:

从理论的角度来看,它很有意思,因为它(currying)简化了lambda演算,只包括那些最多只有一个参数的函数.从实际角度来看,它允许程序员通过修复前k个参数从基函数生成函数族.它类似于在墙上固定一些需要两个引脚的东西.在被钉住之前,物体可以自由地移动到表面上的任何地方; 但是,当第一个引脚插入时,运动受到约束.最后,当放入第二个销时,不再有任何移动自由度.类似地,当程序员讨论两个参数的函数并将其应用于第一个参数时,该功能受一个维度的限制.最后,当他将新函数应用于第二个参数时,则计算特定值.

进一步说,我看到函数式编程实质上引入了"数据流编程而不是控制流",这类似于使用SQL而不是C#.有了这个定义,我就会明白LINQ为什么以及为什么它有许多纯Linq2Object之外的应用程序 - 比如Rx中的事件.