web-dev-qa-db-fra.com

Scala: Quelle est la différence entre les traits Traversable et Iterable dans les collections Scala?

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?

91
Rahul

Pour le dire simplement, les itérateurs gardent l'état, les traversables non.

Un Traversable a 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'un autre côté, un Iterable a comme méthode abstraite iterator, qui retourne un Iterator. Vous pouvez appeler next sur un Iterator pour 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.

113
Daniel C. Sobral

Considérez-le comme la différence entre souffler et sucer.

Lorsque vous avez appelé un Traversables foreach, ou ses méthodes dérivées, il insufflera ses valeurs dans votre fonction une par une - afin qu'il ait le contrôle sur l'itération.

Cependant, avec le Iterator renvoyé par un Iterable, vous en extrayez les valeurs, en contrôlant vous-même quand passer à la suivante.

217
Duncan McGregor

tl; drIterables sont Traversables qui peuvent produire des états Iterators


Tout d'abord, sachez que Iterable est un sous-portrait de Traversable.

Seconde,

  • Traversable nécessite l'implémentation de la méthode foreach, qui est utilisée par tout le reste.

  • Iterable nécessite l'implémentation de la méthode iterator, qui est utilisée par tout le reste.

Par exemple, l'implémentation de find pour Traversable utilise foreach (via a pour la compréhension) et lève une exception BreakControl 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 soustraction Iterable remplace cette implémentation et appelle find sur le Iterator, ce qui arrête simplement l'itération 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 lever d'exceptions pour l'itération Traversable, mais c'est la seule façon d'itérer partiellement lorsque vous utilisez simplement foreach.

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


En résumé, Iterable fournit un moyen de suspendre, reprendre ou arrêter l'itération via un Iterator avec état. 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 l'interface plus générale. Mais si vous avez besoin d'un contrôle plus personnalisé de l'itération, vous aurez besoin d'un Iterator, que vous pouvez récupérer à partir d'un Iterable.

20
Paul Draper

La réponse de Daniel sonne bien. Laissez-moi voir si je peux le dire avec mes propres mots.

Un Iterable peut donc vous donner un itérateur, qui vous permet de parcourir les éléments un par un (en utilisant next ()), et de vous arrêter et de continuer comme bon vous semble. 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 parcourir 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 Traversable. Mais Range (1, 10) en tant qu'itérable vous donne un itérateur qui doit utiliser 3 entiers pour l'état, dont l'un est un index.

Étant donné 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

0
user11595225