Quali sono i vantaggi di next-iterator su questo iteratore?

8

Non lavoro troppo spesso con gli iteratori Java / C # direttamente, ma quando lo faccio mi chiedo sempre quale sia stata la ragione per progettare gli iteratori nella "prossima" moda.

Per iniziare devi spostare l'iteratore, per verificare se ci sono dei dati devi controllare se c'è un elemento successivo.

Per me il concetto più attraente è questo iteratore: inizia "dall'inizio" e puoi controllare questo stato, diciamo isValid . Quindi il ciclo su tutta la raccolta sarebbe simile a questo:

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

Come qui per verificare se c'è un elemento successivo, vai lì e poi controlli se lo stato è valido.

YMMV ma comunque - c'è qualche vantaggio di next-iterator (come in Java / C #) su questo-iteratore?

Nota: questa è una domanda concettuale relativa al design della lingua.

    
posta greenoldman 03.02.2014 - 17:58
fonte

2 risposte

6

Penso che un vantaggio del modello MoveNext() di C # /. NET rispetto al modello hasNext() di Java sia che il primo implica che si possa fare del lavoro. Il metodo hasNext() implica un semplice controllo dello stato. Ma cosa succede se stai iterando un flusso lento e devi andare in un database per determinare se ci sono dei risultati? In questo caso, la prima chiamata a hasNext() potrebbe essere una lunga operazione di blocco. La denominazione del metodo hasNext() può essere molto fuorviante su ciò che sta effettivamente accadendo.

Per un esempio reale, questo problema di progettazione mi ha messo in difficoltà quando implementavo le API Reactive Extensions (Rx) di Microsoft in Java. In particolare, per l'iteratore creato da IObservable.asIterable() per funzionare correttamente, il metodo hasNext() dovrebbe bloccare e attendere l'arrivo della successiva notifica di oggetto / errore / completamento. Questo non è affatto intuitivo: il nome implica un semplice controllo dello stato. Contrariamente al design C #, dove MoveNext() dovrebbe bloccare, il che non è un risultato del tutto inaspettato quando si ha a che fare con un flusso valutato pigramente.

Il mio punto è questo: il modello "next-iterator" è preferibile al modello "this-iterator" perché il modello "this-iterator" è spesso una bugia: potrebbe essere richiesto di pre-recuperare il prossimo elemento in per verificare lo stato dell'iteratore. Un design che comunica chiaramente questa possibilità è preferibile, secondo me. Sì, le convenzioni di denominazione di Java seguono il modello "successivo", ma le implicazioni comportamentali sono simili a quelle del modello "this-iterator".

Chiarimento: Forse contrastare Java e C # è stata una scelta sbagliata, perché il modello di Java è ingannevole. Ritengo che la distinzione più importante nei modelli presentati dall'OP sia che il modello "this-iterator" disaccoppia la logica "è valida" dalla logica "recupera corrente / prossimo elemento", mentre un l'implementazione ideale "next iterator" combina queste operazioni. Ritengo che sia opportuno combinarli perché, in alcuni casi, determinare se lo stato dell'iteratore è valido richiede il prefetching del prossimo elemento , in modo che la possibilità sia resa il più esplicita possibile.

Non vedo una differenza significativa di design tra:

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!)
}

Ma qui vedo una differenza significativa:

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

In entrambi gli esempi isValid() e hasNext() , l'operazione implicita per essere un controllo dello stato veloce e non bloccante può essere in realtà un'operazione di blocco lento . Nell'esempio moveNext() , la maggior parte del lavoro viene eseguita nel metodo che ti aspetti, indipendentemente dal fatto che tu abbia a che fare con un flusso valutato con interesse o pigramente.

    
risposta data 03.02.2014 - 18:02
fonte
7

Se c'è un calcolo da fare per calcolare ogni valore che deve ottenere il valore successivo prima di chiedere il primo valore, è possibile rinviare l'elaborazione di quel primo valore oltre la costruzione dell'iteratore.

Ad esempio, l'iteratore potrebbe rappresentare una query che non è stata ancora eseguita. Quando viene richiesto il primo elemento, il database viene interrogato per i risultati. Usando il tuo progetto proposto, quel calcolo dovrebbe essere fatto sulla costruzione dell'iteratore (che rende più difficile rinviare l'esecuzione) o quando si chiama IsValid , nel qual caso è davvero un termine improprio, poiché chiamare IsValid sarebbe in realtà ottenendo il valore successivo.

    
risposta data 03.02.2014 - 18:02
fonte

Leggi altre domande sui tag