Scala: Quelle est la différence entre les traits Traversables et Itérables dans les collections Scala?

98

J'ai regardé cette question mais je ne comprends toujours pas la différence entre les traits Iterable et Traversable. Quelqu'un peut-il expliquer?

Rahul
la source
2
Il n'y en a plus Traversabledans Scala 2.13 (il est toujours conservé comme alias obsolète Iterablejusqu'à 2.14)
Xavier Guihot

Réponses:

121

Pour faire simple, les itérateurs conservent l'état, les traversables non.

A Traversablea une méthode abstraite: foreach. Lorsque vous appelez foreach, la collection alimentera la fonction passée tous les éléments qu'elle conserve, l'un après l'autre.

D'autre part, an Iterablea comme méthode abstraite iterator, qui retourne un Iterator. Vous pouvez faire appel nextà un Iteratorpour obtenir l'élément suivant au moment de votre choix. Jusqu'à ce que vous le fassiez, il doit garder une trace de l'endroit où il se trouvait dans la collection et de la suite.

Daniel C. Sobral
la source
4
Mais Iterables'étend Traversable, donc je suppose que vous voulez dire Traversables qui ne sont pas Iterables.
Robin Green
4
@RobinGreen Je veux dire que se conformer à l' Traversableinterface ne nécessite pas de garder l'état, alors que se conformer à l' Iteratorinterface le fait.
Daniel C. Sobral
10
TraversableLes s qui Iterablene conservent aucun état d'itération. C'est le Iteratorcréé et retourné par le Iterablequi garde l'état.
Graham Lea
1
Il convient de noter qu'à partir de la version 2.13, le trait Traversable est obsolète. Pour citer Stefan Zeiger, "L'abstraction Traversable n'a pas porté son poids dans la bibliothèque actuelle et ne refera probablement pas surface dans le nouveau design. Tout ce que nous voulons faire peut être exprimé avec Iterable."
Igor Urisman
226

Pensez-y comme la différence entre souffler et sucer.

Lorsque vous appelez un Traversables foreach, ou ses méthodes dérivées, il insuffle ses valeurs dans votre fonction une à la fois - il a donc le contrôle sur l'itération.

Avec le Iteratorretourné par un Iterablecependant, vous en aspirez les valeurs, en contrôlant vous-même le moment de passer à la suivante.

Duncan McGregor
la source
49
Les gens ont l'habitude d'appeler cela pousser et tirer au lieu de souffler et de sucer , mais j'aime votre ouverture d'esprit.
Martijn
2
Ne jamais oublier cela lorsqu'on me le demande lors de ma prochaine interview
thestephenstanton
23

tl; dr Iterables sont Traversablesqui peuvent produire avec étatIterators


Premièrement, sachez que Iterablec'est un sous-portrait de Traversable.

Seconde,

  • Traversablenécessite l'implémentation de la foreachméthode, qui est utilisée par tout le reste.

  • Iterablenécessite l'implémentation de la iteratorméthode, qui est utilisée par tout le reste.

Par exemple, l'implémentation de findfor Traversableuses foreach(via un for comprehension) et lève une BreakControlexception pour arrêter l'itération une fois qu'un élément satisfaisant a été trouvé.

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

En revanche, la Iterablesoustraction remplace cette implémentation et appelle findle Iterator, qui arrête simplement d'itérer une fois l'élément trouvé:

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

Ce serait bien de ne pas lancer d'exceptions pour l' Traversableitération, mais c'est la seule façon d'itérer partiellement en utilisant juste foreach.

D'un point de vue, Iterablec'est le trait le plus exigeant / puissant, car vous pouvez facilement implémenter l' foreachutilisation iterator, mais vous ne pouvez pas vraiment implémenter l' iteratorutilisation foreach.


En résumé, Iterablefournit un moyen de suspendre, de reprendre ou d'arrêter l'itération via un état Iterator. Avec Traversable, c'est tout ou rien (sans exceptions pour le contrôle de flux).

La plupart du temps, cela n'a pas d'importance et vous voudrez une interface plus générale. Mais si jamais vous avez besoin d'un contrôle plus personnalisé sur l'itération, vous aurez besoin d'un Iterator, que vous pouvez récupérer à partir d'un fichier Iterable.

Paul Draper
la source
1

La réponse de Daniel sonne bien. Voyons si je peux le dire avec mes propres mots.

Ainsi, un Iterable peut vous donner un itérateur, qui vous permet de parcourir les éléments un à la fois (en utilisant next ()), et de s'arrêter et de partir à votre guise. Pour ce faire, l'itérateur doit garder un "pointeur" interne sur la position de l'élément. Mais un Traversable vous donne la méthode, foreach, pour traverser tous les éléments à la fois sans s'arrêter.

Quelque chose comme Range (1, 10) n'a besoin que de 2 entiers comme état comme Traversable. Mais Range (1, 10) en tant que Iterable vous donne un itérateur qui doit utiliser 3 entiers pour l'état, dont l'un est un index.

Considérant que Traversable propose également foldLeft, foldRight, son foreach doit traverser les éléments dans un ordre connu et fixe. Il est donc possible d'implémenter un itérateur pour un Traversable. Par exemple, def iterator = toList.iterator

user11595225
la source