Expression.Reduce()是做什么的?

40
我已经研究表达式树几天了,我很想知道Expression.Reduce()是什么。 msdn文档并没有太大帮助,因为它只说明它可以“减少”表达式。 以防万一,我尝试了一个示例(请参见下文),以检查此方法是否包含数学约简,但似乎不是这种情况。
有人知道这个方法是做什么的吗? 是否可以提供一个快速示例来演示其工作原理? 有好的资源吗?
static void Main(string[] args)
{
    Expression<Func<double, double>> func = x => (x + x + x) + Math.Exp(x + x + x);
    Console.WriteLine(func);
    Expression r_func = func.Reduce();
    Console.WriteLine(r_func); // This prints out the same as Console.WriteLine(func)
}

你的示例可能存在缺陷。检查 CanReduce 来确定 reduce 调用是否真正起作用。 - Anon.
3
可以,它会返回false。换句话说,我的问题是:什么情况下Expression.CanReduce会返回true? - d..
当表达式可以简化为“更简单”的表达式时。我对“更简单”的猜测将基于内部表示 - “更简单”的表达式是具有较小和/或更快的内部表示的表达式。 - Anon.
1
同样的猜测 - 因此提出了这个问题。 - d..
嘿,Boris,你是如何做到那样的?(例如,我如何添加一个超链接将URL隐藏在某些字符串后面?)-谢谢! - d..
5个回答

35
你需要查看的文档是expr-tree-spec.pdf。这是表达式树的规范。请阅读“2.2可约节点”和“4.3.5 Reduce方法”部分。基本上,这个方法旨在帮助实现或移植动态语言到.NET的人们。这样他们就可以创建自己的节点,这些节点可以“简化”到标准的表达式树节点并进行编译。在表达式树API中有一些“可约”节点,但我不知道是否有任何实际的示例(因为所有标准表达式节点都可以编译,作为终端用户,您可能不关心它们在幕后是否被“约简”)。是的,MSDN文档在这个领域非常基础,因为语言实现者的主要信息和文档来源是在GitHub上,文档在其子文件夹中。

这是很棒的东西。感谢您提供的资源(顺便说一句,博客写得很好)。 - d..
如果微软能够提到在哪里找到“真正”的文档,那么缺乏文档也就无所谓了。 - Qwertie
2
在这种情况下,是我的错。我忘记将链接添加到 System.LINQ.Expressions 的 CodePlex 中,只添加到了 System.Dynamic。可以将其视为“文档错误”。我会尝试在下一次文档更新中修复它,通常每月更新一次。 - Alexandra Rusina
1
文档已经移动到Github,网址为https://github.com/IronLanguages/dlr/blob/master/Docs/expr-tree-spec.pdf。已编辑。 - Zev Spitz

25

通过一些反汇编,我发现 Expression.CanReduce 总是返回 false,而 Expression.Reduce() 总是返回 this。但是,有一些类型重写了这两个方法。LambdaExpression 继承了默认实现,这就解释了为什么到目前为止尝试过的表达式都不起作用。

其中一个重写了 Reduce() 的类型是 MemberInitExpression,这导致我进行了以下成功的实验:

class ReduceFinder : ExpressionVisitor {
    public override Expression Visit(Expression node) {
        if (node != null && node.CanReduce) {
            var reduced = node.Reduce();
            Console.WriteLine("Found expression to reduce!");
            Console.WriteLine("Before: {0}: {1}", node.GetType().Name, node);
            Console.WriteLine("After: {0}: {1}", reduced.GetType().Name, reduced);
        }
        return base.Visit(node);
    }
}

class Foo {
    public int x;
    public int y;
}

static class Program {
    static void Main() {
        Expression<Func<int, Foo>> expr = z => new Foo { x = (z + 1), y = (z + 1) };
        new ReduceFinder().Visit(expr);
    }
}

输出:

Found expression to reduce!  
Before: MemberInitExpression: new Foo() {x = (z + 1), y = (z + 1)}  
After: ScopeN: { ... }  

很遗憾,不过我也会接受亚历山德拉的答案,所以不用担心。 :) 如果你还没有投票,你可以给两个答案都点赞... - Nick Guerrera
我也不行,显然我得注册,我会注册并给你点赞(天啊,又一个我要注册的网站...) - d..
1
谢谢!至少 Stack Overflow 使用 OpenID! ;) - Nick Guerrera

9

这是一个相当老的问题,但似乎有一些兴趣,因此我添加了额外的回复,提供有关现在的.NET开箱即用内容的信息。

据我所知,Reduce()仅在实现赋值作为其工作一部分的复杂操作中被覆盖。似乎有三个关键场景。

  1. Compound assignments are expanded to discrete binary arithmetic and assignment operations; in other words,

    x += y

    becomes

    x = x + y.

  2. Pre-increment and post-increment operators are expanded to their discrete operations. For pre-increment/decrements,

    ++x

    becomes approximately:

    x = x + 1

    and

    x++

    becomes approximately:

    temp = x;
    x = x + 1;
    temp;
    

    I say approximately because the operation is not implemented as a binary operation x + 1 with the left operand being x and the right operand being the constant 1 but as a unary increment/decrement operation. The net effect is the same.

  3. Member and list initializers are expanded from their short form to their long form. So:

    new Thing() { Param1 = 4, Param2 = 5 }

    becomes:

    temp = new Thing();
    temp.Param1 = 4;
    temp.Param2 = 5;
    temp;
    

    and:

    new List<int>() { 4, 5 }

    becomes:

    temp = new List<int>();
    temp.Add(4);
    temp.Add(5);
    temp;
    
无论这些更改是否使得一个人更容易或更难实现解析表达式树的功能,都是一个有争议的问题,但归根结底,这似乎是.NET框架中默认提供的简化程度。

4

1

我猜这更多是为了不同的Linq提供程序使用它们来将某些节点类型转换为更简单的AST表示。

由于文档很少,可以用于公共子表达式消除以消除冗余表达式。如果您的函数计算x+x超过一次而没有改变本地x,则可以通过将第一个表达式的结果保存到临时变量中来简化它。也许由Linq提供程序可选地实现这些转换。

或者,如果您有包含没有代码的嵌套BlockExpressions(例如{{{}}}这样的表达式),则可以消除这些表达式,或者是空的ConditionalExpression...


我刚刚将 x 的值减少了 => (x + x + x) + Math.Exp(x + x + x),但似乎也没有任何效果... - d..

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接