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

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

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

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


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

Во-вторых,

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

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

Например, для реализации find для Traversable используется foreach (через a для понимания) и генерирует исключение 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 является более требовательным/мощным признаком, так как вы легко можете реализовать foreach с помощью iterator, но вы не можете реализовать iterator с помощью foreach.


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

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

Ответ 4

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

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

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

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