Scala: Jaka jest różnica między cechami Traversable i Iterable w kolekcjach Scala?

98

Przyjrzałem się temu pytaniu, ale nadal nie rozumiem różnicy między cechami iterowalnymi i przemiennymi. Czy ktoś może wyjaśnić?

Rahul
źródło
2
Nie ma więcej Traversablew Scali 2.13 (jest nadal przechowywany jako przestarzały alias Iterabledo 2.14)
Xavier Guihot

Odpowiedzi:

121

Mówiąc prościej, iteratory zachowują stan, a trawersable nie.

TraversableMa jeden abstrakcyjny sposób: foreach. Kiedy wywołasz foreach, kolekcja dostarczy przekazanej funkcji wszystkie elementy, które przechowuje, jeden po drugim.

Z drugiej strony Iterablemetoda ma as abstrakcyjną iterator, która zwraca Iterator. Możesz wezwać nexta, Iteratoraby uzyskać następny element w wybranym przez siebie momencie. Dopóki tego nie zrobisz, musi śledzić, gdzie był w kolekcji i co dalej.

Daniel C. Sobral
źródło
4
Ale Iterablerozciąga się Traversable, więc myślę, że masz na myśli, Traversableże nie są Iterable.
Robin Green
4
@RobinGreen Mam na myśli, że zgodność z Traversableinterfejsem nie wymaga utrzymywania stanu, podczas gdy zgodność z Iteratorinterfejsem wymaga.
Daniel C. Sobral
10
Traversablete, które są Iterable, nie zachowują żadnego stanu iteracji. To Iteratorstworzone i zwrócone przez to, Iterableco utrzymuje stan.
Graham Lea,
1
Warto zauważyć, że od 2.13 cecha Traversable jest przestarzała. Cytując Stefana Zeigera: „Abstrakcja Traversable nie miała swojego znaczenia w obecnej bibliotece i prawdopodobnie nie pojawi się ponownie w nowym projekcie. Wszystko, co chcemy zrobić, można wyrazić za pomocą Iterable”.
Igor Urisman
226

Potraktuj to jako różnicę między dmuchaniem a ssaniem.

Kiedy wywołasz a Traversables foreachlub metody pochodne, będzie on wysyłał wartości do Twojej funkcji pojedynczo - dzięki czemu będzie miał kontrolę nad iteracją.

Z Iteratorpowrotem przez Iterablejednak wysysasz z niego wartości, kontrolując, kiedy samemu przejść do następnej.

Duncan McGregor
źródło
49
Ludzie nazywają to pchaniem i ciągnięciem zamiast dmuchaniem i ssaniem , ale lubię twoją otwartość.
Martijn
2
Nigdy o tym nie zapominam, gdy pytam o to w moim następnym wywiadzie
thestephenstanton
23

tl; dr Iterables są, Traversablesktóre mogą generować stanoweIterators


Po pierwsze, wiedz, że Iterableto jest cecha Traversable.

Druga,

  • Traversablewymaga implementacji foreachmetody, która jest używana przez wszystko inne.

  • Iterablewymaga implementacji iteratormetody, która jest używana przez wszystko inne.

Na przykład implementacja findfor Traversableuse foreach(za pomocą a for compearing) i zgłasza BreakControlwyjątek, aby zatrzymać iterację po znalezieniu zadowalającego elementu.

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

W przeciwieństwie do tego, Iterableodejmowania nadpisuje to wdrożenie i rozmów findw sprawie Iterator, która po prostu przestaje iteracji po znalezieniu element:

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

Byłoby miło nie rzucać wyjątków dla Traversableiteracji, ale to jedyny sposób na częściowe iterowanie, gdy używasz just foreach.

Z jednej perspektywy Iterablejest bardziej wymagającą / potężną cechą, ponieważ można ją łatwo wdrożyć foreachza pomocą iterator, ale tak naprawdę nie można zaimplementować iteratorza pomocą foreach.


Podsumowując, Iterablezapewnia sposób na wstrzymanie, wznowienie lub zatrzymanie iteracji za pomocą stanu Iterator. Z Traversable, to wszystko albo nic (bez wyjątków kontroli przepływu).

W większości przypadków nie ma to znaczenia, a będziesz chciał mieć bardziej ogólny interfejs. Ale jeśli kiedykolwiek będziesz potrzebować bardziej spersonalizowanej kontroli nad iteracją, będziesz potrzebować pliku Iterator, który możesz pobrać z pliku Iterable.

Paul Draper
źródło
1

Odpowiedź Daniela brzmi dobrze. Zobaczmy, czy potrafię ująć to własnymi słowami.

Tak więc Iterable może dać ci iterator, który pozwala przechodzić elementy jeden po drugim (przy użyciu next ()) i zatrzymywać się i kontynuować, jak chcesz. Aby to zrobić, iterator musi utrzymywać wewnętrzny „wskaźnik” na pozycję elementu. Ale Traversable daje ci metodę, dla każdego, do przechodzenia przez wszystkie elementy naraz bez zatrzymywania się.

Coś w rodzaju Range (1, 10) musi mieć tylko 2 liczby całkowite jako stan jako Traversable. Ale Range (1, 10) jako Iterable daje iterator, który musi użyć 3 liczb całkowitych dla stanu, z których jedna jest indeksem.

Biorąc pod uwagę, że Traversable oferuje również foldLeft, foldRight, jego foreach musi przejść przez elementy w znanej i ustalonej kolejności. Dlatego można zaimplementować iterator dla Traversable. Np. Def iterator = toList.iterator

user11595225
źródło