2016-10-29 3 views
1

Click here to see the problem statement imageScala Iteratoren sind verwirrend

Ich habe versucht, sehr schwer zu verstehen, warum Iteratoren so verhalten. Ich meine nach einmaliger Ausführung

result = lines.filter(_.nonEmpty).map(_.toInt) 

der Iterator-Puffer ist mit allen Elementen außer dem letzten Element überschrieben.

Ich meine, wenn ich 5 Elemente in meiner Eingangstextdatei habe nach 5 mal geben

result = lines.filter(_.nonEmpty).map(_.toInt) 

mein Iterator immer leer.

Jede Hilfe ist sehr geschätzt .... Vielen Dank im Voraus

+0

@ victor-moroz Ich sehe jetzt, Sie demonstrierten die Gotcha. Die Antwort lautet also: "Ja, Iteratoren sind sehr verwirrend, wenn Sie sie missbrauchen." Wir haben Begriffe wie "Fail-Fast" zu sagen, "Wenn ich Sie missbrauche, bitte explodieren Sie in einer Weise, die ich leicht debuggen kann." –

Antwort

0

som-snytt ist hier richtig, aber nicht erklären, was genau los war.

Wenn Sie einen Iterator transformieren, müssen Sie das Ergebnis der Umwandlung speichern und nur verwenden. Insbesondere wird das Aufrufen von filter auf einem Iterator intern zwischengespeichert, wodurch next auf dem ursprünglichen Iterator aufgerufen und in einer head Variablen gespeichert wird. Wenn Sie next auf dem gepufferten Ding anrufen, erhalten Sie 4. Wenn Sie next auf dem ursprünglichen Iterator aufrufen, erhalten Sie 8: Ihr erstes Element ist weg. Wenn Sie stattdessen geschrieben hat:

var result = lines.filter(_.nonEmpty).map(_.toInt) 
var result = result.filter(_.nonEmpty).map(_.toInt) 
var result = result.filter(_.nonEmpty).map(_.toInt) 

Sie konnten die letzte Zeile so oft wiederholen, wie Sie wollen, ohne der Iterator immer leer, weil Sie immer auf dem transformierten Iterator arbeiten.

EDIT: die Puffer Kommentar adressieren - hier ist der Code für Iterator.filter:

def filter(p: A => Boolean): Iterator[A] = new AbstractIterator[A] { 
    private var hd: A = _ 
    private var hdDefined: Boolean = false 

    def hasNext: Boolean = hdDefined || { 
    do { 
     if (!self.hasNext) return false 
     hd = self.next() 
    } while (!p(hd)) 
    hdDefined = true 
    true 
    } 

    def next() = if (hasNext) { hdDefined = false; hd } else empty.next() 
} 

Die hd und hdDefined Variablen durchführen genau die gleiche Pufferung, die in Iterator.buffered verwendet wird.

+0

'.filter 'puffert nichts, wenn Sie es im Iterator aufrufen, aber wenn Sie' hasNext' auf '.filter' aufrufen, werden einige (alle) Elemente des ursprünglichen Iterators zerstört. Was passiert, wenn Sie es in REPL tun, da REPL 'hasNext' aufruft. Ich hatte eine Antwort, die erklärte, wie es tatsächlich funktioniert, aber eine solche Erklärung ist irrelevant. Tu es einfach nicht. –

+0

'Iterator.filter' Code widerspricht jedoch direkt Ihrer Aussage. Der Aufruf von '.filter' gibt einfach ein neues Objekt zurück, die Pufferung erfolgt in' hasNext' und 'next'. –

+0

Nicht sicher, was der Codeschnipsel zu demonstrieren bedeutet, aber es ist erwähnenswert, dass bestimmte Methoden auf bestimmten Iteratoren möglicherweise nicht destruktiv sind, aber das sind Implementierungsdetails, und Sie dürfen sich nie darauf verlassen. –