C# Linq 与柯里化(Currying)

标签 c# linq currying

我正在研究函数式编程及其各种概念。所有这些东西都很有趣。我已经多次阅读有关 Currying 及其优势的文章。

但我不明白这一点。以下源代码演示了 curry 概念的使用和 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();
}

这是高阶过滤方法:

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

最佳答案

what is the advantage of using currying?

首先,让我们澄清一些术语。人们使用“currying”来表示两者:

  1. 将两个参数的方法重新表述为一个返回一个参数的方法的一个参数的方法
  2. 部分应用两个参数的方法生成一个参数的方法

显然,这两项任务密切相关,因此容易混淆。在正式发言时,应该将“currying”限制为指代第一个定义,但在非正式发言时,这两种用法都很常见。

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

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

你可以这样调用它:

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

你可以 curry Add方法:

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

现在:

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

部分应用有时也被非正式地称为“currying”,因为它有明显的相关性:

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

你可以制造一台机器来为你完成这个过程:

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

那么现在我们来回答您的问题:

what is the advantage of using currying?

这两种技术的优势在于,它可以让您更灵活地处理方法。

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

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

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

Func<Point, double> distanceFinder = PartiallyApply<Point, Point, double>(ApproximateDistance, givenEndPoint);

如果首先柯里化(Currying) ApproximateDistance 方法:

static Func<Point, double> MakeApproximateDistanceFinder(Point p1) { ... }

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

Func<Point, double> distanceFinder = MakeApproximateDistanceFinder(givenEndPoint);

关于C# Linq 与柯里化(Currying),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8826266/

相关文章:

c# - nancyfx - 从父文件夹提供静态文件

c# - 如果遇到不止一次,使自动映射器缓存对象

c# - TakeWhile,但我也想要输入序列的其余部分

f# - 反向柯里化(Currying)?

methods - 在 Python 3 中将方法分配给 var 时如何自动使用 self currying?

c# - 简单的客户端/服务器,TCP/IP 加密消息流,SSL

c# - DataContract 不会反序列化 Id DataMember

c# - 使用 LINQ 获取多个节点值

linq - EF1.0 中的预加载

ruby - 为什么 Ruby 中的柯里化(Currying)在我的代码中不起作用?