Expression.Reduce() 做啥?
Posted
技术标签:
【中文标题】Expression.Reduce() 做啥?【英文标题】:What does Expression.Reduce() do?Expression.Reduce() 做什么? 【发布时间】:2011-01-03 13:41:19 【问题描述】:我已经使用表达式树几天了,我很想知道 Expression.Reduce() 的作用。 msdn documentation 不是很有帮助,因为它只声明它“减少”了表达式。为了以防万一,我尝试了一个示例(见下文)来检查此方法是否包含数学归约,但似乎并非如此。
有谁知道这个方法是做什么的,是否可以提供一个简单的例子来展示它的作用?有什么好的资源吗?
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 调用是否真的会做任何事情。
当然,它返回 false。换句话说,我的问题是:Expression.CanReduce 什么时候返回 true?
当表达式可以简化为“更简单”的表达式。我对“更简单”的猜测将基于内部表示 - “更简单”的表达式是具有更小和/或更快的内部表示的表达式。
这里的猜测相同 - 因此问题。
嘿,鲍里斯,你是怎么做到的? (即如何添加一个超链接,将 URL 隐藏在某个字符串后面?) - 谢谢!
【参考方案1】:
您需要查看的文档是expr-tree-spec.pdf。
这是表达式树的规范。阅读“2.2 可约节点”和“4.3.5 约简方法”部分。
基本上,此方法适用于实现动态语言或将其移植到 .NET 的人员。这样他们就可以创建自己的节点,这些节点可以“减少”为标准表达式树节点并且可以编译。表达式树 API 中有一些“可简化”节点,但我不知道您是否可以获得任何实际示例(因为所有标准表达式节点无论如何都可以编译,作为最终用户,您可能并不关心它们是否被“简化” " 幕后与否)。
是的,MSDN 文档在这方面非常基础,因为语言实现者的主要信息和文档来源在 GitHub,文档在 its own subfolder。
【讨论】:
这是很棒的东西。也非常感谢您提供的资源(顺便说一句,很棒的博客)。 如果微软不屑于提及在哪里可以找到“真正的”文档,那么缺少文档就可以了... 在这种特殊情况下,这是我的错。我忘记将 codeplex 的链接添加到 System.LINQ.Expressions,只将其添加到 System.Dynamic。将其视为“文档错误”。我会尝试在下一次文档更新时修复它,通常每月更新一次。 文档已移至 Github,地址为 github.com/IronLanguages/dlr/blob/master/Docs/…。已编辑。【参考方案2】:稍微反汇编一下,我发现 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: ...
【讨论】:
很遗憾没有,但我也会接受亚历山德拉的,所以不用担心。 :) 但是,如果您还没有投票,您可以对这两个答案都投赞成票... 我也不能,显然我必须注册,我会做并投票给你(jezz,我注册的另一个网站......) 谢谢!至少 Stack Overflow 使用 OpenID! ;)【参考方案3】:这是一个相当老的问题,但似乎有点兴趣,所以我添加了这个额外的回复,其中包含关于开箱即用的 .NET 东西目前的作用的信息。
据我所知,Reduce() 仅在将赋值作为其工作的一部分实现的复杂操作中被覆盖。似乎存在三个关键场景。
复合赋值扩展为离散二进制算术和赋值运算;换句话说,
x += y
变成
x = x + y
.
前自增和后自增运算符扩展为它们的离散运算。对于预增量/减量,
++x
变成近似:
x = x + 1
和
x++
变成近似:
temp = x;
x = x + 1;
temp;
我之所以这么说是因为该操作没有实现为二元操作x + 1
,左操作数为x
,右操作数为常量1
,但作为一元递增/递减操作。最终效果是一样的。
成员和列表初始值设定项从它们的短形式扩展为长形式。所以:
new Thing() Param1 = 4, Param2 = 5
变成:
temp = new Thing();
temp.Param1 = 4;
temp.Param2 = 5;
temp;
和:
new List<int>() 4, 5
变成:
temp = new List<int>();
temp.Add(4);
temp.Add(5);
temp;
这些变化是否使人们更容易或更难实现解析表达式树的东西是一个见仁见智的问题,但最重要的是,这似乎是 .NET 中开箱即用的减少级别框架。
【讨论】:
【参考方案4】:除了Nick Guerrera 的回答,我发现以下表达式覆盖了CanReduce
方法:
* 根据 JustDecompile 表示 BinaryExpression
的内部派生类型
【讨论】:
【参考方案5】:我猜测不同的 linq 提供者使用它们将某些节点类型转换为更简单的 ast 表示形式。
由于文档很少,可用于common subexpression elimination 以消除冗余表达式。如果您的函数在不更改本地 x 的情况下多次计算 x+x,则可以通过将第一个表达式的结果保存到临时文件中来简化它。也许由 linq 提供者来选择实现这些转换。
或者如果您嵌套了不包含任何代码的 BlockExpressions(类似 的表达式),则可以消除这些代码,或者是一个空的 ConditionalExpression...
【讨论】:
我只是减少了 x => (x + x + x) + Math.Exp(x + x + x) 但它似乎也没有做任何事情......以上是关于Expression.Reduce() 做啥?的主要内容,如果未能解决你的问题,请参考以下文章
天蓝色流分析查询太复杂?没有给出正确的输出,现在出错。接下来做啥