简体   繁体   English

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

[英]How do I avoid infinite recursion with a custom enumerator?

I made an extension method to find the number of consecutive values in a collection. 我做了一个扩展方法来查找集合中连续值的数量。 Because it is generic, I allow the caller to define the "incrementor" which is a Func<> that is supposed to increment the value in order to check for the existence of a "next" value. 因为它是通用的,所以我允许调用者定义“incrementor”,它是一个Func <>,它应该增加值以检查是否存在“next”值。

However, if the caller passes an improper incrementor (ie x => x), it will cause an infinite recursive loop. 但是,如果调用者传递了一个不正确的递增器(即x => x),它将导致无限递归循环。 Any suggestions on a clean way to prevent this? 有什么建议干净的方法来防止这种情况?

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;
}

To deal with the simplest case, you can do this: 要处理最简单的情况,您可以这样做:

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

For general case, do this: 一般情况下,请执行以下操作:

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;
}

You can compare nextValue to startValue (you'll need T to implement IComparable). 您可以将nextValue与startValue进行比较(您需要T来实现IComparable)。

This will solve this bug, it won't solve a nasty incrementor bug that returns a loop - a1, a2, a3, ..., an, a1. 这将解决这个错误,它不会解决一个令人讨厌的增量错误,它返回一个循环 - a1,a2,a3,...,an,a1。 I don't think you want to handle this case, though 不过,我认为你不想处理这种情况

In the purest sense, this is an attempt at the Halting Problem and is undecidable. 从最纯粹的意义上说,这是对停止问题的一种尝试,并且是不可判定的。 For all but the simplest cases, you'll have to trust those calling your method. 除了最简单的情况之外,你必须要相信那些调用你方法的人。

Like others have shown, you can do a simple check of equality to show that the next value is different. 像其他人已经表明的那样,您可以对相等性进行简单检查,以显示下一个值是不同的。 Storing every visited T will work but you'll have to worry about memory eventually . 存储每个访问过的T都可以,但最终你不得不担心内存。

As an aside, here's an easily implemented StackOverflowException so you have to be wary of any data set that will have a lot on consecutive values. 顺便说一句,这是一个容易实现的StackOverflowException,因此您必须警惕任何在连续值上有很多的数据集。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM