2011-12-01 15 views
6

Hice un método de extensión para encontrar el número de valores consecutivos en una colección. Debido a que es genérico, le permito a la persona que llama definir el "incrementor" que es un Func <> que se supone que incrementa el valor para verificar la existencia de un "próximo" valor.¿Cómo evito la recursión infinita con un enumerador personalizado?

Sin embargo, si la persona que llama pasa un incremento impropio (es decir, x => x), producirá un bucle recursivo infinito. ¿Alguna sugerencia sobre una forma limpia de prevenir esto?

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

La solución simple es suponer que la persona que escribe la persona que llama está en su sano juicio. A veces solo tienes que culpar a la persona que está sobre ti. Sin embargo, esta es una pregunta interesante, por lo que me gustaría ver lo que otras personas pueden aportar. – Polynomial

+1

[Detener el problema] (http://en.wikipedia.org/wiki/Halting_problem)? –

+0

Si su IEnumerable es grande y contiguo (incrementor dado), esto es susceptible a StackOverflowExceptions. –

Respuesta

0

En el sentido más puro, este es un intento en el Halting Problem y es indecidible. Para todos los casos menos simples, tendrá que confiar en los que llaman a su método.

Como han demostrado otros, puede hacer una simple comprobación de igualdad para mostrar que el siguiente valor es diferente. Almacenar cada visita T funcionará pero tendrá que preocuparse por la memoria eventualmente.

Como un lado, aquí hay una StackOverflowException fácilmente implementada por lo que debe tener cuidado con cualquier conjunto de datos que tendrá mucho en valores consecutivos.

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

¿El desbordamiento de la pila está causado por la profundidad de la recursión? –

+0

Sí. En el ejemplo, todos son consecutivos dado 'x => x + 1' y esto se desbordará alrededor de 77K (en mi máquina). –

1

Puede comparar nextValue to startValue (necesitará T para implementar IComparable).

Esto resolverá este error, no resolverá un desagradable error incrementor que devuelve un bucle - a1, a2, a3, ..., an, a1. Creo que no desea manejar este caso, sin embargo

+1

no es necesario implementar IComparable; todo lo que necesita es igualdad –

4

Para tratar el caso más simple, se puede hacer esto:

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

Por caso general, hacer esto:

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: dado que incrementor se define como Func , apenas tendría sentido si devolviera el mismo valor. A menos que, por supuesto, mantenga algún tipo de conteo de llamadas internas estáticas y esté hecho para devolver algo como 1,1,2,2,3,3, pero me sorprendería que se requiriera el apoyo de tal incremento. Parece razonable requerir que incrementor (x)! = X y también para que sea acíclico. –

Cuestiones relacionadas