¿Cuáles son las ventajas del próximo iterador sobre este iterador?

8

No trabajo muy a menudo con los iteradores de Java / C # directamente, pero cuando lo hago, siempre me pregunto cuál fue la razón para diseñar los iteradores de la siguiente manera.

Para comenzar debes mover el iterador, para verificar si hay algunos datos que debes verificar si hay el siguiente elemento.

El concepto más atractivo para mí es este iterador: se "inicia" desde el principio, y puedes verificar este estado, digamos isValid . Así que el bucle sobre toda la colección se vería así:

while (iter.isValid())
{
  println(iter.current());
  iter.next();
}

Como usted está aquí para verificar si hay un elemento siguiente, vaya allí y luego verifique si el estado es válido.

YMMV pero de todos modos, ¿hay alguna ventaja de next-iterator (como en Java / C #) sobre este iterador?

Nota: esta es una pregunta conceptual, sobre el diseño del idioma.

    
pregunta greenoldman 03.02.2014 - 17:58

2 respuestas

6

Creo que una ventaja del modelo MoveNext() de C # /. NET sobre el modelo hasNext() de Java es que el primero implica que se puede realizar algún trabajo. El método hasNext() implica una simple verificación de estado. Pero, ¿qué sucede si está iterando un flujo lento y tiene que ir a una base de datos para determinar si hay resultados? En ese caso, la primera llamada a hasNext() puede ser una larga operación de bloqueo. La denominación del método hasNext() puede ser muy engañosa en cuanto a lo que realmente está sucediendo.

Para un ejemplo del mundo real, este problema de diseño me mordió al implementar las API de Microsoft Reative Extensions (Rx) en Java. Específicamente, para que el iterador creado por IObservable.asIterable() funcione correctamente, el método hasNext() tendría que bloquear y esperar a que llegue la siguiente notificación de elemento / error / finalización. Eso es difícilmente intuitivo: el nombre implica una simple comprobación de estado. Contrasta esto con el diseño de C #, donde MoveNext() tendría que bloquear, lo que no es un resultado totalmente inesperado cuando se trata de un flujo evaluado de forma perezosa.

Mi punto es este: el modelo de "siguiente iterador" es preferible al modelo de "este-iterador" porque el modelo de "este-iterador" es a menudo una mentira: es posible que se le solicite que busque previamente el siguiente elemento en Para comprobar el estado del iterador. Un diseño que comunica claramente esa posibilidad es preferible, en mi opinión. Sí, las convenciones de nomenclatura de Java siguen el modelo "siguiente", pero las implicaciones de comportamiento son similares a las del modelo "this-iterator".

Aclaración: Quizás contrastar Java y C # fue una mala elección, porque el modelo de Java es engañoso. Considero que la distinción más importante en los modelos presentados por el OP es que el modelo de "este iterador" desacopla la lógica "es válida" de la lógica de "recuperar el elemento actual / siguiente", mientras que La implementación ideal del "siguiente iterador" combina estas operaciones. Creo que es apropiado combinarlos porque, en algunos casos, determinar si el estado del iterador es válido requiere seleccionar previamente el siguiente elemento , por lo que esa posibilidad debería hacerse lo más explícita posible.

No veo una diferencia de diseño significativa entre:

while (i.isValid()) { // do we have an element?  (implied as fast, non-blocking)
    doSomething(i.current()); // retrieve the element (may be slow!)
    i.next();
}
// ...and:
while (i.hasNext()) { // do we have an element?  (implied as fast, non-blocking)
    doSomething(i.next()); // retrieve the element (may be slow!)
}

Pero veo una diferencia significativa aquí:

while (i.hasNext()) { // do we have an element?  (implied as fast, non-blocking)
    doSomething(i.next()); // retrieve the element (may be slow!)
}
// ...and:
while (i.moveNext()) { // fetch the next element if it exists (may be slow!)
    doSomething(i.current()); // get the element  (implied as fast, non-blocking)
}

En los ejemplos de isValid() y hasNext() , la operación que se supone es una comprobación de estado rápida y sin bloqueo de hecho puede ser una operación de bloqueo lenta . En el ejemplo de moveNext() , la mayor parte del trabajo se realiza en el método que usted esperaría, independientemente de si se trata de un flujo evaluado con entusiasmo o perezosamente.

    
respondido por el Mike Strobel 03.02.2014 - 18:02
7

Si hay algún cálculo que debe hacerse para calcular cada valor que tiene que obtener el siguiente valor antes de pedir el primer valor, le permite diferir el procesamiento de ese primer valor más allá de la construcción del iterador.

Por ejemplo, el iterador podría representar una consulta que aún no se ha ejecutado. Cuando se solicita el primer elemento, se consultan los resultados en la base de datos. Usando su diseño propuesto, ese cálculo debería hacerse al construir el iterador (lo que dificulta el aplazamiento de la ejecución) o al llamar a IsValid , en cuyo caso es realmente un nombre inapropiado, ya que llamar a IsValid sería obteniendo el siguiente valor.

    
respondido por el Servy 03.02.2014 - 18:02

Lea otras preguntas en las etiquetas