¿Cuáles son las ventajas del siguiente iterador sobre este iterador?

8

No trabajo con demasiada frecuencia con los iteradores 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, debe mover el iterador, para verificar si hay algunos datos, debe verificar si hay un elemento siguiente.

El concepto más atractivo para mí es este iterador: "comienza" desde el principio, y puede verificar este estado, digamos isValid. Entonces el ciclo de toda la colección se vería así:

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

Como usted 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 del siguiente iterador (como en Java / C #) sobre este iterador?

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

Greenoldman
fuente
la pregunta es para Java o C # bzoc isEl método válido no existe en Java
@NiksTyagi Es una pregunta conceptual que se aplica por igual a ambos, ya que el diseño propuesto es diferente de la implementación de cualquiera de los idiomas.
Servicio
Cada llamada virtual es costosa. Su ejemplo tiene tres llamadas, .net solo dos. Personalmente, prefiero reducirlo a una sola llamada.
CodesInChaos

Respuestas:

6

Creo que una ventaja del MoveNext()modelo de C # /. NET sobre el modelo de Java hasNext()es que el primero implica que se puede hacer algún trabajo. El hasNext()método implica una simple verificación de estado. Pero, ¿qué sucede si está iterando una secuencia diferida y tiene que ir a una base de datos para determinar si hay algún resultado? En ese caso, la primera llamada a hasNext()puede ser una operación de bloqueo larga. El nombramiento del hasNext()método puede ser muy engañoso 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 Extensiones Reactivas (Rx) de Microsoft en Java. Específicamente, para que el iterador creado por IObservable.asIterable()funcione correctamente, el hasNext()método tendría que bloquear y esperar a que llegue la siguiente notificación de elemento / error / finalización. Eso no es intuitivo: el nombre implica una simple verificación de estado. Compare eso con el diseño de C #, donde MoveNext()tendría que bloquear, lo que no es un resultado totalmente inesperado cuando se trata de una secuencia evaluada de manera 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 deba buscar previamente el siguiente elemento para verificar El estado del iterador. Un diseño que comunique esa posibilidad claramente 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 "este iterador".


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 de "es válido" de la lógica de "recuperar el elemento actual / próximo", mientras que es una implementación ideal de "siguiente iterador" combina estas operaciones. Creo que es apropiado combinarlos porque, en algunos casos, determinar si el estado del iterador es válido requiere la captación previa del siguiente elemento , por lo que esa posibilidad debe 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)
}

Tanto en el ejemplo isValid()como en el hasNext()ejemplo, la operación que implica una verificación de estado rápida y sin bloqueo puede, de hecho, ser una operación de bloqueo lenta . En el moveNext()ejemplo, la mayor parte del trabajo se realiza en el método que esperaría, independientemente de si se trata de una secuencia evaluada con impaciencia o con pereza.

Mike Strobel
fuente
1
0: Creo OP trató de comparar Java / C # vs. algún otro iterador (C # IEnumrable y Java Iterator punto de "antes" elementos cuando se creó). Comprendo la pregunta es por qué no señalar el primer elemento en lugar de comparar C # vs.Java.
Alexei Levenkov
Gracias por la respuesta, pero C # y Java usan el mismo modelo (ambos se centran en el siguiente elemento).
greenoldman
Los modelos C # y Java definitivamente no son lo mismo. Vea mi ejemplo agregado. El modelo de C # combina las operaciones de verificar la presencia de un elemento con la obtención de ese elemento. Java los divide en operaciones separadas, y la verificación de presencia a veces requiere en realidad buscar el artículo debajo del capó. En realidad, es bastante similar al ejemplo del OP: si ignora los nombres de los métodos, hasNext()es similar a isValid(), y next()es similar a current().
Mike Strobel
Me refería a modelo en el sentido de ESTA pregunta. Ok, no son idénticos, pero ambos se basan en el siguiente elemento, cuando iteras en ambos tienes que ir al siguiente elemento al inicio, porque ambos comienzan antes de la colección real.
greenoldman
Mantengo que el modelo Java es muy similar al modelo "this-iterator" en la pregunta de cómo funciona realmente . Afirmo que las implicaciones de comportamiento entre "this-iterator" y "next-iterator" son más importantes que las convenciones de nomenclatura, y el modelo Java tiene implicaciones de comportamiento similares a su ejemplo "this-iterator". Si bien mi respuesta puede haberse desviado hacia un territorio que el OP no había considerado al hacer la pregunta, no creo que eso haga que mi respuesta sea menos válida.
Mike Strobel
7

Si hay que hacer algún cálculo para calcular cada valor, tener 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 consulta la base de datos para obtener los resultados. Usando su diseño propuesto, ese cálculo tendría que hacerse luego de la construcción del iterador (lo que hace que sea más difícil aplazar la ejecución) o al llamar IsValid, en cuyo caso es realmente un nombre inapropiado, ya que llamar IsValidrealmente obtendría el siguiente valor.

Servy
fuente
Buena respuesta. Llegaste al mismo punto que yo, pero me ganaste un par de segundos. Tener un voto a favor :).
Mike Strobel
1
Gracias por la respuesta, pero creo que es incorrecta. Para verificar si el iterador es válido, no tiene que obtener el elemento real, solo la posición del iterador. IOW tiene el mismo impacto que hasNext, solo su estado sobre el estado actual, no el futuro. O tomando otra vista: compare esto con los iteradores de C ++, verifique si el estado es válido (current!=end)sin tocar los datos reales.
greenoldman
PD. Peguemos su escenario: me gustaría imprimir los resultados, así que tengo que llamar hasNextal principio, aquí se ejecuta la consulta, se recuperan los datos. En el segundo modelo, llamo isValid, aquí se ejecuta la consulta. Entonces, el punto de ejecución de la consulta es el mismo.
greenoldman
@greenoldman Entonces, al llamar, IsValidlo que realmente estás haciendo es obtener el siguiente valor. Esto significa que está haciendo exactamente lo mismo que el iterador existente (no tiene el actual válido desde el principio, sin necesidad de llamar a un método para obtenerlo) solo tiene un nombre erróneo.
Servy
1
Exactamente. El modelo de "siguiente iterador" es preferible al modelo de "este iterador" porque el modelo de "este iterador" a menudo es una mentira: es posible que deba buscar previamente el siguiente elemento para verificar el estado del iterador .
Mike Strobel