Scala: В чем разница между трейтами Traversable и Iterable в коллекциях Scala?

98

Я рассмотрел этот вопрос, но до сих пор не понимаю разницы между чертами Iterable и Traversable. Кто-нибудь может объяснить?

Рахул
источник
2
Существует не более Traversableв Scala 2.13 (она до сих пор хранится в качестве псевдонима для уходящего Iterableдо 2.14)
Ксавье Guihot

Ответы:

121

Проще говоря, итераторы сохраняют состояние, а обходные пути - нет.

TraversableИмеет один абстрактный метод: foreach. Когда вы вызываете foreach, коллекция будет кормить переданную функцию всеми элементами, которые она хранит, один за другим.

С другой стороны, у an Iterableесть абстрактный метод iterator, который возвращает Iterator. Вы можете позвонить nextпо принципу , Iteratorчтобы получить следующий элемент в момент вашего выбора. Пока вы этого не сделаете, он должен отслеживать, где он был в коллекции и что будет дальше.

Дэниел С. Собрал
источник
4
Но Iterableрасширяется Traversable, так что я думаю, вы имеете в виду Traversables, которые не Iterables.
Робин Грин
4
@RobinGreen Я имею в виду, что соблюдение Traversableинтерфейса не требует сохранения состояния, в то время как соблюдение Iteratorинтерфейса требует.
Дэниел С. Собрал
10
Traversables, которые Iterableне сохраняют состояние итерации. Это Iteratorсозданный и возвращенный объект, Iterableкоторый сохраняет состояние.
Graham Lea
1
Стоит отметить, что с версии 2.13 трейт Traversable устарел. По словам Стефана Зейгера, «абстракция Traversable не получила должного веса в текущей библиотеке и, скорее всего, не появится в новом дизайне. Все, что мы хотим сделать, можно выразить с помощью Iterable».
Игорь Урисман
226

Думайте об этом как о разнице между дутьем и сосанием.

Когда вы вызываете Traversables foreachили его производные методы, он будет передавать свои значения в вашу функцию по одному, поэтому он имеет контроль над итерацией.

С Iteratorвозвращением Iterableмысли вы высасываете из него значения, контролируя, когда переходить к следующему.

Дункан МакГрегор
источник
49
Люди используют , чтобы назвать это толкая и потянув вместо того , чтобы дуть и сосать , но я , как ваш непредвзятости.
Martijn
2
Никогда не забываю об этом, когда его спросят в моем следующем интервью
thestephenstanton
23

tl; dr Iterables , Traversablesкоторые могут создаватьIterators


Во-первых, знайте, что Iterableэто вычитание Traversable.

Во-вторых,

  • Traversableтребует реализации foreachметода, который используется всем остальным.

  • Iterableтребует реализации iteratorметода, который используется всем остальным.

Например, реализация findfor Traversableиспользует foreach(через for понимание) и выдает BreakControlисключение, чтобы остановить итерацию, как только будет найден удовлетворительный элемент.

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

Напротив, Iterableвычитание переопределяет эту реализацию и вызывает findметод Iterator, который просто прекращает повторение после того, как элемент найден:

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

Было бы неплохо не генерировать исключения для Traversableитерации, но это единственный способ частичной итерации при использовании только foreach.

С одной стороны, Iterableэто более требовательная / мощная черта, поскольку вы можете легко реализовать foreachusing iterator, но не можете реализовать iteratorusing foreach.


Таким образом, Iterableпредоставляет способ приостановить, возобновить или остановить итерацию с помощью состояния Iterator. С участиемTraversable этом все или ничего (без исключений для управления потоком).

В большинстве случаев это не имеет значения, и вам понадобится более общий интерфейс. Но если вам когда-нибудь понадобится более индивидуальный контроль над итерацией, вам понадобится файл Iterator, который можно получить из файла Iterable.

Пол Дрейпер
источник
1

Ответ Даниэля звучит хорошо. Дайте мне посмотреть, смогу ли я выразить это своими словами.

Таким образом, Iterable может предоставить вам итератор, который позволяет вам перемещаться по элементам по одному (используя next ()), а также останавливаться и идти в любое время. Для этого итератору необходимо сохранить внутренний «указатель» на позицию элемента. Но Traversable дает вам метод foreach для одновременного обхода всех элементов без остановки.

Что-то вроде Range (1, 10) должно иметь только 2 целых числа в качестве состояния Traversable. Но Range (1, 10) как Iterable дает вам итератор, который должен использовать 3 целых числа для состояния, одно из которых является индексом.

Учитывая, что Traversable также предлагает foldLeft, foldRight, его foreach должен перемещаться по элементам в известном и фиксированном порядке. Следовательно, можно реализовать итератор для Traversable. Например, def iterator = toList.iterator

user11595225
источник