如何使用自定义枚举器避免无限递归?

时间:2011-12-01 20:25:36

标签: c# recursion

我做了一个扩展方法来查找集合中连续值的数量。因为它是通用的,所以我允许调用者定义“增量”,它是一个Func<>应该增加值以检查是否存在“下一个”值。

但是,如果调用者传递了一个不正确的递增器(即x => x),它将导致无限递归循环。有关干净的方法的任何建议,以防止这种情况?

public static int CountConsecutive<T>(this IEnumerable<T> values, T startValue, Func<T, T> incrementor)
{
    if (values == null)
    {
        throw new ArgumentNullException("values");
    }
    if (incrementor == null)
    {
        throw new ArgumentNullException("incrementor");
    }
    var nextValue = incrementor(startValue);
    return values.Contains(nextValue)
        ? values.CountConsecutive(nextValue, incrementor) + 1
        : 1;
}

3 个答案:

答案 0 :(得分:4)

要处理最简单的情况,您可以这样做:

var nextValue = incrementor(startValue);
if (nextValue.Equals(startValue)) {
    throw new ArgumentException("incrementor");
}

一般情况下,请执行以下操作:

public static int CountConsecutive<T>(this IEnumerable<T> values, T startValue, Func<T, T> incrementor) {
    if (values == null) {
        throw new ArgumentNullException("values");
    }
    if (incrementor == null) {
        throw new ArgumentNullException("incrementor");
    }
    ISet<T> seen = new HashSet<T>();
    return CountConsecutive(values, startValue, incrementor, seen);
}

private static int CountConsecutive<T>(IEnumerable<T> values, T startValue, Func<T, T> incrementor, ISet<T> seen) {
    if (!seen.Add(startValue)) {
        throw new ArgumentException("incrementor");
    }
    var nextValue = incrementor(startValue);
    return values.Contains(nextValue)
        ? values.CountConsecutive(nextValue, incrementor) + 1
        : 1;
}

答案 1 :(得分:1)

您可以将nextValue与startValue进行比较(您需要T来实现IComparable)。

这将解决这个错误,它不会解决一个令人讨厌的增量错误,它返回一个循环 - a1,a2,a3,...,an,a1。我认为您不想处理案例,但

答案 2 :(得分:0)

从最纯粹的意义上说,这是Halting Problem的尝试而且是不可判定的。对于除了最简单的情况之外的所有情况,您都必须相信那些调用您方法的人。

与其他人一样,您可以对等式进行简单检查,以显示下一个值是不同的。存储每个访问过的T都可以,但您最终必须担心内存

顺便说一下,这是一个容易实现的StackOverflowException,所以你必须警惕任何连续值都有很多的数据集。

var x = Enumerable.Range(1, 100000).CountConsecutive(1, x => x+1);