Lassen Sie mich ein bisschen auf Rex Kerr ‚s answer erweitern. Es gibt wirklich drei zentrale Klassen für alle Scala-Sammlung, und nur zwei von ihnen sind wirklich Teil der Sammlungen. Sie sind Traversable
, Iterable
und Iterator
.
Die grundlegendste Sammlung ist die Traversable
. Es gibt nur eine Voraussetzung für etwas, um ein Traversable
zu sein: es muss die Methode foreach
implementieren. Solange also eine Funktion übergeben werden kann, die dann auf jedes Element der Sammlung angewendet wird, kann es sich um eine Traversable
handeln. Zum Beispiel:
class Three[A](a: A, b: A, c: A) extends Traversable[A] {
def foreach[U](f: (A) => U) {
f(a); f(b); f(c)
}
}
Dies wird Ihnen alle Traversable
Methoden geben, obwohl Methoden wie map
und filter
keinen Three
zurück, sondern eine Traversable
. Es ist viel schwieriger, die von Ihnen definierte Klasse zurückzugeben, und viele spezialisierte Klassen können das einfach nicht. Zum Beispiel, Three
kann es nicht tun, denn was wäre die Three
einer filter
, die einige Elemente entfernt?
Als nächstes gibt es die Iterator
, die wirklich so ziemlich die gleiche Sache wie Traversable
tut, aber auf andere Weise. Eine Iterator
muss zwei Methoden definieren: next
und hasNext
.Zum Beispiel:
class Three[A](a: A, b: A, c: A) extends Iterator[A] {
private var aReady, bIsRead, cIsRead = false
def hasNext = !(aIsRead && bIsRead && cIsRead)
def next = (aIsRead, bIsRead, cIsRead) match {
case (false, _, _) => aIsRead = true; a
case (_, false, _) => bIsRead = true; b
case (_, _, false) => cIsRead = true; c
case _ => Iterator.empty.next
}
}
Dadurch werden alle Methoden der Iterator
, geben die meist die gleichen wie die Methoden in Traversable
suchen. Der Unterschied zwischen den Methoden hängt hauptsächlich damit zusammen, dass ein Iterator
nur einmal verwendet werden kann.
Schließlich gibt es Iterable
. Um eine Iterable
zu sein, muss eine Klasse eine Methode implementieren: iterator
, die eine Iterator
für diese Klasse zurückgibt. Zum Beispiel:
class Three[A](a: A, b: A, c: A) extends Iterable[A] {
// not necessary, but may offer a more efficient implementation
override def foreach[U](f: (A) => U) {
f(a); f(b); f(c)
}
def iterator = new Iterator[A] {
private var aReady, bIsRead, cIsRead = false
def hasNext = !(aIsRead && bIsRead && cIsRead)
def next = (aIsRead, bIsRead, cIsRead) match {
case (false, _, _) => aIsRead = true; a
case (_, false, _) => bIsRead = true; b
case (_, _, false) => cIsRead = true; c
case _ => Iterator.empty.next
}
}
}
Also, zurück zu Ihrer Frage, müssen Sie überlegen, was das erwartete Verhalten ist, dass Sie wollen, und wie wollen Sie es. Insbesondere gibt es in Scala-Sammlungen keine Vorstellung von "down" und "left", was bedeutet, dass Node
eine Methode haben könnte, die eine Scala-Sammlung zurückgibt, aber niemals wäre, wie wir auf Rex Kerrs Lösung sehen.
EDIT
mich Lassen Sie uns ein Beispiel geben sich von Rex Kerr. Hier mache ich eine Traversable
Node
, und die Reihenfolge der Traversierung wird wählbar sein.
class Node[A] extends Traversable[A] {
var left: Node[A] = _
var down: Node[A] = _
var traverseLeft = true
var value: A = _
def foreach[U](f: (A) => U) = if (traverseLeft) foreachLeft(f) else foreachDown(f)
def foreachLeft[U](f: (A) => U) { f(value); if (left != null) left.foreachLeft(f) }
def foreachDown[U](f: (A) => U) { f(value); if (down != null) down.foreachDown(f) }
}
Also das Node
ist Traversable
und unterstützt alle Traversable
Methoden (obwohl es noch kein Node
von map
usw. zurückkehren - andere Fragen zu diesem Thema nachschlagen). Sie können auswählen, ob es mit einem Flag (traverseLeft
) nach links oder nach unten durchlaufen wird, und alle normalen Traversable
Methoden verwenden das, was im Knoten festgelegt ist, auf dem die Methode aufgerufen wurde.
Dies ist jedoch kein gutes Modell. Ich würde lieber mit Rex Kerrs Lösung der zurückkehrenden Iteratoren nach links und nach unten gehen oder die Scala-Sammlungen vollständig verlassen und mit etwas arbeiten, das mit Kiama verarbeitet wurde. Letzteres ist jedoch ein völlig anderes Paradigma, und nicht etwas, das Sie in einen Code umwandeln, der nach Scala transkribiert wird.
Natürlich kann ich das tun. Aber es gibt eine Menge Code, der sich in jedem Fall wiederholt - und genau das versuche ich zu vermeiden. Das Problem ist ein bisschen schlimmer als ich beschrieben habe, da es vielleicht 6-10 Klassen mit jeweils mindestens einer verketteten Liste gibt, so dass ich als Mimum das Verhalten oben auf ein Merkmal verallgemeinern möchte - das habe ich bis jetzt nicht geschafft obwohl. Ich würde auch in der Lage sein, für Comprehensions, foreach usw. zu verwenden (bis jetzt habe ich gefunden, es gibt Entsprechungen für 'finden',' foreach', 'forall',' exists' und 'entspricht') –
Für links/unten , nur 'Eigenschaft DoubleIterable [A] {var links: A; var down: A;/* Iterator defs hier * /} 'und dann' Class Node erweitert DoubleIterable [Node] '? –
Ja. Noch mehr Duplizierung als ich es möchte. –