2

我不确定这是否是严格的柯里化,但我基本上想要实现以下目标。给定一个Expression

Expression<Func<T1, T2, TResult>> expression

我想传入其中一个参数并生成一个对应的参数,Expression其中该参数的值是固定的。生成的表达式应该在功能上等同于,expression除了它应该少一个参数。

这个结果表达式看起来像这样:

Expression<Func<T2, TResult>> curriedExpression;

我已经尝试过了,但它不起作用,因为 anExpression不会隐式转换为 lambda 表达式:

curriedExpression = b => expression(fixedValueForT1, b);

请注意,curriedExpression不应包含对expression;的调用。它应该包含重复的逻辑,除了固定值。

我希望这是有道理的。让我知道这是否模棱两可或没有得到很好的解释。

4

3 回答 3

3

我认为您可以ExpressionVisitor以简单的方式从该类中派生。这是一个概念证明 - 它可能过于简单化,但我认为这就是你所追求的:

using System;
using System.Linq.Expressions;

class Test
{
    static void Main()
    {
        Expression<Func<int, int, int>> original = (x, y) => MethodX(x) + MethodY(y);
        Console.WriteLine("Original: {0}", original);
        var partiallyApplied = ApplyPartial(original, 10);
        Console.WriteLine("Partially applied: {0}", partiallyApplied);
    }

    static int MethodX(int x)
    {
        return x + 1;
    }

    static int MethodY(int x)
    {
        return -x;
    }

    static Expression<Func<T2, TResult>> ApplyPartial<T1, T2, TResult>
        (Expression<Func<T1, T2, TResult>> expression, T1 value)
    {
        var parameter = expression.Parameters[0];
        var constant = Expression.Constant(value, parameter.Type);
        var visitor = new ReplacementVisitor(parameter, constant);
        var newBody = visitor.Visit(expression.Body);
        return Expression.Lambda<Func<T2, TResult>>(newBody, expression.Parameters[1]);
    }
}

class ReplacementVisitor : ExpressionVisitor
{
    private readonly Expression original, replacement;

    public ReplacementVisitor(Expression original, Expression replacement)
    {
        this.original = original;
        this.replacement = replacement;
    }

    public override Expression Visit(Expression node)
    {
        return node == original ? replacement : base.Visit(node);
    }
}

输出:

Original: (x, y) => (MethodX(x) + MethodY(y))
Partially applied: y => (MethodX(10) + MethodY(y))
于 2013-10-03T06:11:54.917 回答
0

这是@jon-skeet 实现的替代方案,具有以下优点/缺点:

优点:

  • 输入表达式可以有 0..n 个任意类型的参数。
  • 您可以通过指定被替换参数的索引来对这些参数中的任何一个进行 curry。

缺点:

  • 您失去了编译时类型安全性(输入表达式没有泛型参数,索引可能超出范围并且替换为object)。
  • 您必须指定返回的 lambda 表达式的类型。
private Expression<TLambda> Curry<TLambda>(
    LambdaExpression searchExpression, 
    int replacedParameterIndex, 
    object replacement)
{
    var parameter = searchExpression.Parameters[replacedParameterIndex];
    var constant = Expression.Constant(replacement, parameter.Type);
    var visitor = new ReplacementVisitor(parameter, constant);
    var newBody = visitor.Visit(searchExpression.Body);
    var lambda = Expression.Lambda<TLambda>(newBody, searchExpression.Parameters.Except(new[] { parameter }));

    return lambda;
}

因此,在@jon-skeet 的示例中,我们将使用:

var partiallyApplied = Curry<int, int>(original, 0, 10);
于 2014-03-10T09:12:06.710 回答
0

我刚刚发现这可能是使用LinqKit 实现的,您可以通过 NuGet here获得它。

我目前没有时间尝试这个例子,但它可能值得研究,所以你不必使用像ExpressionVisitor.

于 2013-11-28T22:33:03.863 回答