¿Cómo diseñas un enumerador que devuelve (teóricamente) una cantidad infinita de elementos?

StackOverflow https://stackoverflow.com/questions/197297

  •  10-07-2019
  •  | 
  •  

Pregunta

Estoy escribiendo un código similar a este:

public IEnumerable<T> Unfold<T>(this T seed)
{
    while (true)
    {
        yield return [next (T)object in custom sequence];
    }
}

Obviamente, este método nunca va a volver. (El compilador de C # lo permite en silencio, mientras que R # me da la advertencia & Quot; La función nunca devuelve & Quot; .)

En términos generales, ¿es un diseño malo proporcionar un enumerador que devuelva un número infinito de elementos, sin proporcionar una forma de dejar de enumerar?

¿Hay alguna consideración especial para este escenario? Mem? Perf? ¿Otros trucos?

Si siempre proporcionamos una condición de salida, ¿cuáles son las opciones? Por ejemplo:

  • un objeto de tipo T que representa el límite inclusivo o exclusivo
  • a Predicate<T> continue (como TakeWhile lo hace)
  • un recuento (como Take lo hace)
  • ...

¿Debemos confiar en que los usuarios llamen Take(...) / TakeWhile(...) después de Unfold(...)? (Tal vez la opción preferida, ya que aprovecha el conocimiento existente de Linq.)

¿Respondería a esta pregunta de manera diferente si el código se publicara en una API pública, ya sea tal cual (genérico) o como una implementación específica de este patrón?

¿Fue útil?

Solución

Mientras documente muy claramente que el método nunca terminará de iterar (el método en sí mismo regresa muy rápido, por supuesto), entonces creo que está bien. De hecho, puede hacer que algunos algoritmos sean mucho más limpios. No creo que haya implicaciones significativas de memoria / rendimiento, aunque si se refiere a un & "; Costoso &"; objeto dentro de su iterador, esa referencia será capturada.

Siempre hay formas de abusar de las API: siempre que tus documentos estén claros, creo que está bien.

Otros consejos

  

" En general, ¿es un mal diseño?   para proporcionar un enumerador que devuelve   una cantidad infinita de artículos, sin   proporcionando una manera de dejar de enumerar? "

El consumidor del código puede siempre dejar de enumerar (usando break, por ejemplo, u otros medios). Si su enumerador regresa y tiene una secuencia infinita, eso no significa que el cliente del enumerador esté forzado de alguna manera a nunca romper la enumeración, en realidad no puede hacer un enumerador que esté garantizado para ser enumerado completamente por un cliente.

  

¿Debemos confiar en los usuarios que llaman   Take (...) / TakeWhile (...) after   Desplegar(...)? (Tal vez el preferido   opción, ya que aprovecha las existentes   Conocimiento Linq.)

Sí, siempre que especifique claramente en su documentación que el enumerador regresa y que la secuencia infinita y la ruptura de la enumeración son responsabilidad de la persona que llama, todo debería estar bien.

Devolver secuencias infinitas no es una mala idea, los lenguajes de programación funcionales lo han hecho durante mucho tiempo.

Estoy de acuerdo con Jon. El compilador transforma su método en una clase que implementa una máquina de estado simple que mantiene la referencia al valor actual (es decir, el valor que se devolverá a través de la propiedad Current). Usé este enfoque varias veces para simplificar el código. Si documenta claramente el comportamiento del método, debería funcionar bien.

No usaría un enumerador infinito en una API pública. Los programadores de C #, incluido yo mismo, están demasiado acostumbrados al bucle foreach. Esto también sería coherente con .NET Framework; observe cómo los métodos Enumerable.Range y Enumerable.Repeat toman un argumento para limitar el número de elementos en el Enumerable. Microsoft eligió usar Enumerable.Repeat (& Quot; & Quot ;, 10) en lugar de Enumerable.Repeat (& Quot; & Quot;). Take (10) para evitar la enumeración infinita y yo se adheriría a sus elecciones de diseño.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top