Я рассмотрел этот вопрос, но до сих пор не понимаю разницы между Iterable и Traversable. Может кто-нибудь объяснить?
Scala: В чем разница между Traversable и Iterable чертами в коллекциях Scala?
Ответ 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