转换为使用递归时出现问题

本文关键字:问题 递归 转换 | 更新日期: 2023-09-27 18:34:41

我有一段代码,它使用嵌套循环创建一个 linq 表达式,该表达式比较对象 Paint 中布尔组合的所有可能性。 它运行良好,但如果我想继续向 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);
}

转换为使用递归时出现问题

你不需要递归来实现这一点,也不需要嵌套循环。我最初误读了这个问题,并认为您只是在做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]; } }
}

上面维护了计数器集,就好像每个计数器都是带有 counterCount 位数字的基数counterMax的数字。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 种可能的组合,包括完全忽略该属性的选项。因此,只需迭代从 0 到 3^N - 1 的计数,使用 MultiCounter 类来维护这个以 3 为底的数字。

虽然这并不像您只有两种状态那样方便,但一件好事是,在完成了额外的工作之后,现在您对可以管理的属性数量没有显着限制。为了达到MultiCounter实现中数组的 2GB 限制,即拥有 2+ 十亿个属性,您必须首先以某种方式绕过许多其他基本限制。:)