Scala: Was ist der Unterschied zwischen traversierbaren und iterierbaren Merkmalen in Scala-Sammlungen?

98

Ich habe mir diese Frage angesehen , verstehe aber immer noch nicht den Unterschied zwischen iterierbaren und traversierbaren Merkmalen. Kann jemand erklären?

Rahul
quelle
2
Es ist nicht mehr Traversablein Scala 2,13 (es immer noch als deprecated alias für gehalten wird , Iterablebis 2.14)
Xavier Guihot

Antworten:

121

Einfach ausgedrückt, Iteratoren behalten den Status bei, Traversables nicht.

A Traversablehat eine abstrakte Methode : foreach. Wenn Sie aufrufen foreach, füttert die Sammlung die übergebene Funktion nacheinander mit allen Elementen, die sie behält.

Auf der anderen Seite hat ein Iterableals abstrakte Methode iterator, die ein zurückgibt Iterator. Sie können rufen nextauf ein Iteratordas nächste Element zum Zeitpunkt Ihrer Wahl zu bekommen. Bis Sie dies tun, muss es verfolgen, wo es in der Sammlung war und was als nächstes kommt.

Daniel C. Sobral
quelle
4
Aber Iterableerstreckt sich Traversable, also denke ich, du meinst Traversables, die nicht Iterables sind.
Robin Green
4
@RobinGreen Ich meine, die Einhaltung der TraversableSchnittstelle erfordert nicht, den Status beizubehalten , während die Einhaltung der IteratorSchnittstelle dies tut.
Daniel C. Sobral
10
Traversables, Iterabledie keinen Iterationsstatus beibehalten. Es ist das, was von dem Iteratorgeschaffen und zurückgegeben wird Iterable, das den Zustand erhält.
Graham Lea
1
Es ist anzumerken, dass Traversable ab 2.13 veraltet ist. Um Stefan Zeiger zu zitieren: "Die Traversable-Abstraktion hat in der aktuellen Bibliothek nicht ihren Stellenwert und wird im neuen Design wahrscheinlich nicht wieder auftauchen. Alles, was wir tun möchten, kann mit Iterable ausgedrückt werden."
Igor Urisman
226

Betrachten Sie es als den Unterschied zwischen Blasen und Saugen.

Wenn Sie ein Traversables foreachoder seine abgeleiteten Methoden aufgerufen haben , werden seine Werte einzeln in Ihre Funktion eingeblendet, sodass es die Kontrolle über die Iteration hat.

Mit dem Iteratorvon einem zurückgegebenen IterablePunkt saugen Sie die Werte heraus und steuern selbst, wann Sie zum nächsten wechseln müssen.

Duncan McGregor
quelle
49
Die Leute nennen das Drücken und Ziehen, anstatt zu blasen und zu saugen , aber ich mag Ihre Aufgeschlossenheit.
Martijn
2
Vergiss das nie, wenn ich in meinem nächsten Interview gefragt werde
theephenstanton
23

tl; dr Iterables sind Traversables, die Stateful produzieren könnenIterators


Erstens wissen, dass Iterabledas Subtrait von ist Traversable.

Zweite,

  • Traversableerfordert die Implementierung der foreachMethode, die von allem anderen verwendet wird.

  • Iterableerfordert die Implementierung der iteratorMethode, die von allem anderen verwendet wird.

Zum Beispiel die Implementierung von findfor- TraversableVerwendungen foreach(über ein zum Verständnis) und löst eine BreakControlAusnahme aus, um die Iteration anzuhalten, sobald ein zufriedenstellendes Element gefunden wurde.

trait TravserableLike {
  def find(p: A => Boolean): Option[A] = {
    var result: Option[A] = None
    breakable {
      for (x <- this)
        if (p(x)) { result = Some(x); break }
    }
    result
  }
}

Im Gegensatz dazu Iterableüberschreibt die Subtraktion diese Implementierung und ruft die findauf Iterator, die einfach aufhört zu iterieren, sobald das Element gefunden wurde:

trait Iterable {
  override /*TraversableLike*/ def find(p: A => Boolean): Option[A] =
    iterator.find(p)
}

trait Iterator {
  def find(p: A => Boolean): Option[A] = {
    var res: Option[A] = None
      while (res.isEmpty && hasNext) {
        val e = next()
        if (p(e)) res = Some(e)
      }
    res
  }
}

Es wäre schön, keine Ausnahmen für die TraversableIteration auszulösen, aber dies ist die einzige Möglichkeit, teilweise zu iterieren, wenn nur verwendet wird foreach.

Aus einer Perspektive, Iterableist die anspruchsvolleren / mächtig Zug, wie Sie leicht umsetzen können foreachmit iterator, aber man kann nicht wirklich implementieren iteratorverwenden foreach.


Zusammenfassend Iterablebietet es eine Möglichkeit, die Iteration über einen Stateful anzuhalten, fortzusetzen oder zu stoppen Iterator. Mit Traversableist es alles oder nichts (ohne Ausnahmen für die Flusskontrolle).

Meistens spielt es keine Rolle, und Sie möchten die allgemeinere Benutzeroberfläche. Wenn Sie jedoch jemals eine individuellere Kontrolle über die Iteration benötigen Iterator, benötigen Sie eine , die Sie von einer abrufen können Iterable.

Paul Draper
quelle
1

Daniels Antwort klingt gut. Lassen Sie mich sehen, ob ich es in meine eigenen Worte fassen kann.

Ein Iterable kann Ihnen also einen Iterator geben, mit dem Sie die Elemente einzeln (mit next ()) durchlaufen und anhalten und loslegen können, wie Sie möchten. Dazu muss der Iterator einen internen "Zeiger" auf die Position des Elements halten. Ein Traversable bietet Ihnen jedoch die Möglichkeit, alle Elemente gleichzeitig zu durchlaufen, ohne anzuhalten.

So etwas wie Range (1, 10) muss nur 2 Ganzzahlen als Status eines Traversable haben. Mit Range (1, 10) als Iterable erhalten Sie jedoch einen Iterator, der 3 Ganzzahlen für den Status verwenden muss, von denen eine ein Index ist.

In Anbetracht dessen, dass Traversable auch foldLeft und foldRight bietet, muss foreach die Elemente in einer bekannten und festen Reihenfolge durchlaufen. Daher ist es möglich, einen Iterator für ein Traversable zu implementieren. ZB def iterator = toList.iterator

user11595225
quelle