无法转换为使用递归

时间:2014-12-19 17:35:28

标签: c# linq recursion expression

我有一段代码使用嵌套循环来创建一个linq表达式,它比较对象Paint中bool组合的所有可能性。它工作得很好,但是如果我想继续添加Properties to Paint,我将不得不继续为进程添加for循环。我想将其转换为使用递归,但我遇到了麻烦。任何人都可以为此提供一些见解/起点吗?

ParameterExpression param = Expression.Parameter(typeof(Paint), "t");

Expression exp = null;

object f = false;
object t = true;

List<Expression> expList = new List<Expression>();

//Properties to compare
MemberExpression memberA = Expression.Property(param, "BoolA");
MemberExpression memberB = Expression.Property(param, "BoolB");
MemberExpression memberC = Expression.Property(param, "BoolC");
MemberExpression memberD = Expression.Property(param, "BoolD");

//Loop 3 times to create expression using BoolA == true, BoolA == false, do not use BoolA
for(int aa = 0; aa <= 2; aa++)
{
    Expression aExp = null;

    if (aa == 0)
    {
        aExp = Expression.Equal(memberA, Expression.Constant(f));
        expList.Add(aExp);
    }
    if(aa == 1)
    {
        aExp = Expression.Equal(memberA, Expression.Constant(t));
        expList.Add(aExp);
    }

    for (int bb = 0; bb <= 2; bb++)
    {
        Expression bExp = null;

        if (bb == 0)
        {
            bExp = Expression.Equal(memberB, Expression.Constant(f));
            expList.Add(bExp);
        }
        if (bb == 1)
        {
            bExp = Expression.Equal(memberB, Expression.Constant(t));
            expList.Add(bExp);
        }

        for(int cc = 0; cc <= 2; cc++)
        {
            Expression cExp = null;

            if (cc == 0)
            {
                cExp = Expression.Equal(memberC, Expression.Constant(f));
                expList.Add(cExp);
            }
            if(cc == 1)
            {
                cExp = Expression.Equal(memberC, Expression.Constant(t));
                expList.Add(cExp);
            }

            for (int dd = 0; dd <= 2; dd++)
            {
                Expression dExp = null;

                if (dd == 0)
                {
                    dExp = Expression.Equal(membeDr, Expression.Constant(f));
                    expList.Add(dExp);
                }
                if (dd == 1)
                {
                    dExp = Expression.Equal(memberD, Expression.Constant(t));
                    expList.Add(dExp);
                }

                //Process expList

                //remove expression to prepare to add its opposite in its place
                expList.Remove(dExp);
            }
            expList.Remove(cExp);
        }
        expList.Remove(bExp);
    }
    expList.Remove(aExp);
}

1 个答案:

答案 0 :(得分:1)

您不需要递归来实现它,也不需要嵌套循环。我最初误解了这个问题,并认为你只是做true / false,即每个属性有两个州。如果是这种情况,你可以利用二进制算术的便利性让常规整数计数器处理状态的枚举。

然而,即使没有这种便利,也不算太糟糕。它仍然是相同的基本技术,但是当属性数量高于63时,你最终会做一些更类似于我提到的抽象的东西。

首先,我们需要抽象的反阶级。这是一个使用字节计数器的版本,这在每个属性状态方面是过度的(支持256个状态而不仅仅是你需要的3个状态),但是使得实现比将多个计数器打包成一个字节或其他更容易类型:

class MultiCounter
{
    private int _counterMax;
    private byte[] _counters;

    public MultiCounter(int counterCount, int counterMax)
    {
        _counterMax = counterMax;
        _counters = new byte[counterCount];
    }

    public bool Increment()
    {
        for (int i = 0; i < _counters.Length; i++)
        {
            if (++_counters[i] < _counterMax)
            {
                return true;
            }

            _counters[i] = 0;
        }

        return false;
    }

    public int this[int index] { get { return _counters[index]; } }
}

以上维护计数器集,好像每个计数器都是counterMax个数字的基数counterCount中的数字。 Increment()方法会增加此基数 - counterMax数字,返回true直到它溢出,这样调用者就可以知道所有可能的组合何时完成。

索引器提供了一种方便的方式来读取每个数字。

现在,我们可以使用这个帮助器类来实现实际的Expression枚举:

MemberExpression[] memberExpressions = 
{
    Expression.Property(param, "BoolA"),
    Expression.Property(param, "BoolB"),
    Expression.Property(param, "BoolC"),
    Expression.Property(param, "BoolD"),
};

MultiCounter multiCounter = new MultiCounter(memberExpressions.Length, 3);

List<Expression> expList = new List<Expression>(memberExpressions.Length);

do
{
    expList.Clear();

    for (int index = 0; index < memberExpressions.Length; index++)
    {
        int propertyCounter = multiCounter[index];

        if (propertyCounter == 2)
        {
            continue;
        }

        expList.Add(Expression.Equal(
            memberExpressions[index],
            Expression.Constant(propertyCounter == 1)));
    }

    // Process expList here

} while (multiCounter.Increment());

(对于拼写错误或其他错误事先道歉......上面的内容仍然只是输入我的浏览器中以供说明之用)。

换句话说,对于N个布尔属性,您有3 ^ N种可能的组合,包括完全忽略该属性的选项。因此,只需使用MultiCounter类重复从0到3 ^ N - 1的计数,即可维护此基数为3的数字。

虽然这不像你只有两个状态那么方便,但有一个好处是完成了额外的工作,现在你对你可以管理的属性数没有明显的限制。要在MultiCounter实现中达到数组的2GB限制,即拥有2亿多个属性,您必须先以某种方式解决其他一些基本限制。 :)