我正在尝试函数式编程及其各种概念.所有这些都非常有趣.我曾几次读过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)
@Eric Lippert评论C#中Currying的优势是什么?(实现部分功能)指向此博客帖子:
在哪里我发现这个最适合我的解释:
从理论的角度来看,它很有意思,因为它(currying)简化了lambda演算,只包括那些最多只有一个参数的函数.从实际角度来看,它允许程序员通过修复前k个参数从基函数生成函数族.它类似于在墙上固定一些需要两个引脚的东西.在被钉住之前,物体可以自由地移动到表面上的任何地方; 但是,当第一个引脚插入时,运动受到约束.最后,当放入第二个销时,不再有任何移动自由度.类似地,当程序员讨论两个参数的函数并将其应用于第一个参数时,该功能受一个维度的限制.最后,当他将新函数应用于第二个参数时,则计算特定值.
进一步说,我看到函数式编程实质上引入了"数据流编程而不是控制流",这类似于使用SQL而不是C#.有了这个定义,我就会明白LINQ为什么以及为什么它有许多纯Linq2Object之外的应用程序 - 比如Rx中的事件.