C#Linq vs. 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, IEnumerable> GetPrimes = HigherOrder.GetFilter().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(); } 

这是HigherOrder过滤方法:

 public static Func<Func, IEnumerable, IEnumerable> GetFilter() { return Filter; } 

使用currying有什么好处?

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

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

 static int Add(int x, int y) { return x + y; } 

你可以这样称呼它:

 int result = Add(2, 3); // 5 

你可以讨论Add方法:

 static Func MakeAdder(int x) { return y => Add(x, y); } 

现在:

 Func addTwo = MakeAdder(2); int result = addTwo(3); // 5 

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

 Func addTwo = y=>Add(2,y); int result = addTwo(3); 

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

 static Func PartiallyApply(Func f, A a) { return (B b)=>f(a, b); } ... Func addTwo = PartiallyApply(Add, 2); int result = addTwo(3); // 5 

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

使用currying有什么好处?

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

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

 static double ApproximateDistance(Point p1, Point p2) { ... } 

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

 Func distanceFinder = PartiallyApply(ApproximateDistance, givenEndPoint); 

如果首先使用了ApproximateDistance方法:

 static Func MakeApproximateDistanceFinder(Point p1) { ... } 

那你就不需要自己做部分应用了; 你只需用固定的终点调用MakeApproximateDistanceFinder ,你就完成了。

 Func distanceFinder = MakeApproximateDistanceFinder(givenEndPoint); 

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

Currying和Partial Function应用

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

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

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

使用currying的优势很大程度上可以在函数式语言中找到,它们是为了受益于currying而构建的,并且具有该概念的方便语法。 C#不是这样的语言,C#中的currying实现通常很难遵循,表达式为HigherOrder.GetFilter().Curry()(IsPrime)