穷举搜索/生成表达式树的每个组合
作者:互联网
我正在使用基本的表达式树优化器来构建查询计划.解析树时,我可以决定如何“最佳”构建它,这取决于可以分配给每个操作的权重.
如果我有一棵简单的树,并且有两种关于如何执行动作的选择,我希望能够生成树的两个变体,然后可以比较每个树的权重以查看最有效的树.
例如,下面的代码将允许我构造“表达式树联接”操作的两种变体:一种具有MergeJoinExpression,另一种具有NestedLoopJoinExpression
class Customer
{
public int Id { get; set; }
}
class Orders
{
public int Id { get; set; }
public int CustomerId { get; set; }
}
class MergeJoinExpresion : JoinExpression
{
}
class NestLoopJoinExpresion : JoinExpression
{
}
class Visitor : ExpressionVisitor
{
public List<Expression> GetPlans(Expression expr)
{
// ???
}
override VisitJoin(JoinExpression join)
{
// For this join, I can return the following (trite example)
// return MergeJoinExpresion
// return NestLoopJoinExpresion
return base.VisitJoin(join);
}
}
我该如何构建一种方法,该方法将生成树的每个变体并将它们返回给我?
class Program
{
static void Main(string[] args)
{
var query = from c in customers
join o in orders on c.Id equals o.CustomerId
select new
{
CustomerId = c.Id,
OrderId = o.Id
};
var plans = new Visitor().GetPlans(query);
}
}
谁能告诉我如何修改Visitor Class GetPlans方法以生成这些变化?
编辑-类似:
class Visitor : ExpressionVisitor
{
private List<Expression> exprs = new List<Expression>();
public List<Expression> GetPlans(Expression expr)
{
Visit(expr);
return exprs;
}
override VisitJoin(JoinExpression join)
{
// For this join, I can return the following (trite example)
// return MergeJoinExpresion
// return NestLoopJoinExpresion
var choices = new Expression[] { MergeJoinExpresion.Create(join), NestLoopJoinExpresion.Create(join) };
foreach(var choice in choices)
{
var cloned = Cloner.Clone(choice);
var newTree = base.VisitJoin(cloned);
exprs.Add(newTree);
}
return base.VisitJoin(join);
}
}
解决方法:
因此,首先,我们将创建一个访客,该访客将帮助我们从表达式中提取JoinExpression对象的列表:
internal class FindJoinsVisitor : ExpressionVisitor
{
private List<JoinExpression> expressions = new List<JoinExpression>();
protected override Expression VisitJoin(JoinExpression join)
{
expressions.Add(join);
return base.VisitJoin(join);
}
public IEnumerable<JoinExpression> JoinExpressions
{
get
{
return expressions;
}
}
}
public static IEnumerable<JoinExpression> FindJoins(
this Expression expression)
{
var visitor = new FindJoinsVisitor();
visitor.Visit(expression);
return visitor.JoinExpressions;
}
接下来,我们将使用以下方法(取自this blog post)来获取序列序列的笛卡尔积:
static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] {item}));
}
接下来,我们将创建一个访问者,该访问者采用一系列表达式对,并用第二个替换成对的第一个表达式的所有实例:
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Dictionary<Expression, Expression> lookup;
public ReplaceVisitor(Dictionary<Expression, Expression> pairsToReplace)
{
lookup = pairsToReplace;
}
public override Expression Visit(Expression node)
{
if(lookup.ContainsKey(node))
return base.Visit(lookup[node]);
else
return base.Visit(node);
}
}
public static Expression ReplaceAll(this Expression expression,
Dictionary<Expression, Expression> pairsToReplace)
{
return new ReplaceVisitor(pairsToReplace).Visit(expression);
}
public static Expression ReplaceAll(this Expression expression,
IEnumerable<Tuple<Expression, Expression>> pairsToReplace)
{
var lookup = pairsToReplace.ToDictionary(pair => pair.Item1, pair => pair.Item2);
return new ReplaceVisitor(lookup).Visit(expression);
}
最后,我们通过查找表达式中的所有联接表达式将所有内容组合在一起,将它们投影到一对序列中,其中JoinExpression是该对中的第一项,第二个是每个可能的替换值.从那里,我们可以采用该值的笛卡尔积来获取所有表达式替换对的所有组合.最后,我们可以将替换的每个组合投影到表达式中,该表达式是实际替换原始表达式中的所有对的结果:
public static IEnumerable<Expression> AllJoinCombinations(Expression expression)
{
var combinations = expression.FindJoins()
.Select(join => new Tuple<Expression, Expression>[]
{
Tuple.Create<Expression, Expression>(join, new NestLoopJoinExpresion(join)),
Tuple.Create<Expression, Expression>(join, new MergeJoinExpresion(join)),
})
.CartesianProduct();
return combinations.Select(combination => expression.ReplaceAll(combination));
}
标签:visitor-pattern,tree,expression,c,expression-trees 来源: https://codeday.me/bug/20191119/2036575.html