其他分享
首页 > 其他分享> > 穷举搜索/生成表达式树的每个组合

穷举搜索/生成表达式树的每个组合

作者:互联网

我正在使用基本的表达式树优化器来构建查询计划.解析树时,我可以决定如何“最佳”构建它,这取决于可以分配给每个操作的权重.

如果我有一棵简单的树,并且有两种关于如何执行动作的选择,我希望能够生成树的两个变体,然后可以比较每个树的权重以查看最有效的树.

例如,下面的代码将允许我构造“表达式树联接”操作的两种变体:一种具有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