Scala: Qual è la differenza tra i tratti Traversable e Iterable nelle collezioni Scala?


Risposte:


121

Per dirla semplicemente, gli iteratori mantengono lo stato, gli attraversabili no.

Una Traversableha un metodo astratto: foreach. Quando si chiama foreach, la raccolta alimenterà alla funzione passata tutti gli elementi che conserva, uno dopo l'altro.

D'altra parte, un Iterableha come metodo astratto iterator, che restituisce un Iterator. Puoi chiamare nextun Iteratorper ottenere l'elemento successivo al momento della tua scelta. Fino a quando non lo fai, deve tenere traccia di dove si trovava nella raccolta e cosa verrà dopo.


4
Ma si Iterableestende Traversable, quindi immagino tu intenda Traversables che non sono Iterables.
Robin Green

4
@RobinGreen Voglio dire che il rispetto Traversabledell'interfaccia non richiede il mantenimento dello stato, mentre il rispetto Iteratordell'interfaccia sì .
Daniel C. Sobral

10
Traversables che sono Iterablenon mantengono alcuno stato di iterazione. È il Iteratorcreato e restituito dal Iterableche mantiene lo stato.
Graham Lea

1
Vale la pena notare che a partire dal 2.13 il tratto Traversable è deprecato. Per citare Stefan Zeiger, "L'astrazione Traversable non ha avuto il suo peso nella libreria attuale e probabilmente non riemergerà nel nuovo design. Tutto ciò che vogliamo fare può essere espresso con Iterable".
Igor Urisman

226

Pensala come la differenza tra soffiare e succhiare.

Quando si chiama una Traversables foreach, o i suoi metodi derivati, soffia i suoi valori nella funzione uno alla volta, quindi ha il controllo sull'iterazione.

Con il Iteratorritorno da un Iterablepensiero, ne risucchi i valori, controllando tu stesso quando passare a quello successivo.


49
La gente lo chiama spingere e tirare invece di soffiare e succhiare , ma mi piace la tua apertura mentale.
Martijn

2
Non dimenticarlo mai quando mi è stato chiesto nella mia prossima intervista
thestephenstanton

23

tl; dr Iterables sono Traversablesche possono produrre statefulIterators


Innanzitutto, sappi che Iterableè un sottotitolo di Traversable.

Secondo,

  • Traversablerichiede l'implementazione del foreachmetodo, che viene utilizzato da tutto il resto.

  • Iterablerichiede l'implementazione del iteratormetodo, che viene utilizzato da tutto il resto.

Ad esempio, l'implementazione di findfor Traversableutilizza foreach(tramite una per la comprensione) e genera BreakControlun'eccezione per arrestare l'iterazione una volta trovato un elemento soddisfacente.

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

Al contrario, la Iterablesottrazione sovrascrive questa implementazione e chiama findil Iterator, che interrompe semplicemente l'iterazione una volta trovato l'elemento:

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

Sarebbe bello non generare eccezioni per l' Traversableiterazione, ma questo è l'unico modo per iterare parzialmente quando si usa solo foreach.

Da un punto di vista, Iterableè il tratto più impegnativo / potente, poiché puoi facilmente implementare foreachusando iterator, ma non puoi davvero implementare iteratorusando foreach.


In sintesi, Iterablefornisce un modo per mettere in pausa, riprendere o interrompere l'iterazione tramite un file stateful Iterator. Con Traversable, è tutto o niente (senza eccezioni per il controllo del flusso).

Il più delle volte non importa e ti consigliamo l'interfaccia più generale. Ma se hai bisogno di un controllo più personalizzato sull'iterazione, avrai bisogno di un file Iterator, che puoi recuperare da un file Iterable.


1

La risposta di Daniel suona bene. Fammi vedere se riesco a dirlo con parole mie.

Quindi un Iterable può darti un iteratore, che ti consente di attraversare gli elementi uno alla volta (usando next ()) e fermarti e andare come preferisci. Per fare ciò, l'iteratore deve mantenere un "puntatore" interno alla posizione dell'elemento. Ma un Traversable ti dà il metodo, foreach, per attraversare tutti gli elementi contemporaneamente senza fermarti.

Qualcosa come Range (1, 10) deve avere solo 2 interi come stato come Traversable. Ma Range (1, 10) come Iterable ti dà un iteratore che deve usare 3 numeri interi per lo stato, uno dei quali è un indice.

Considerando che Traversable offre anche foldLeft, foldRight, il suo foreach deve attraversare gli elementi in un ordine noto e fisso. Pertanto è possibile implementare un iteratore per un Traversable. Ad esempio def iterator = toList.iterator

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.