Скала: Яка різниця між ознаками, які можна подолати та відміняти, у колекціях Scala?


98

Я розглянув це питання, але досі не розумію різниці між ознаками, які можна відмінити і відміняти. Може хтось пояснить?


2
Існує не більше Traversableв Scala 2.13 (вона до сих пір зберігається в якості псевдоніма для минає Iterableдо 2.14)
Ксав'є Guihot

Відповіді:


121

Простіше кажучи, ітератори зберігають стан, траверси - ні.

TraversableМає один абстрактний метод: foreach. Коли ви телефонуєте foreach, колекція передаватиме переданій функції всі елементи, які вона зберігає, один за одним.

З іншого боку, Iterableмає як абстрактний метод iterator, який повертає Iterator. Ви можете зателефонувати next, Iteratorщоб отримати наступний елемент під час вашого вибору. Поки ви цього не зробите, він повинен відслідковувати, де він був у колекції, і що далі.


4
Але Iterableпоширюється Traversable, тому я думаю, ви маєте на увазі Traversables, які не є Iterables.
Робін Грін

4
@RobinGreen Я маю на увазі, що дотримання Traversableінтерфейсу не вимагає збереження стану, а дотримання Iteratorінтерфейсу.
Даніель К. Собрал

10
Traversables, які Iterableне зберігають жодного ітераційного стану. Це Iteratorстворене та повернене тим, Iterableщо утримує державу.
Грем Леа

1
Варто зазначити, що станом на 2,13 ознака "Прохідність" є застарілою. Цитуючи Стефана Цайгера, "Абстрактна абстракція не набула своєї ваги в нинішній бібліотеці і, швидше за все, не з'явиться в новому дизайні. Все, що ми хочемо зробити, може бути виражене Ітерабельним".
Ігор Урісман

226

Подумайте про це як про різницю між видуванням та смоктанням.

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

З Iteratorповерненим, Iterableоднак, ви висмоктуєте з нього значення, контролюючи, коли самостійно перейти до наступного.


49
Люди називають це штовханням та тягненням замість того, щоб дути і смоктати , але мені подобається ваша відкритість.
Martijn

2
Ніколи не забуваючи цього, коли мене запитали в наступному моєму інтерв'ю
thestephenstanton

23

tl; dr - Iterables це Traversablesте , що може призвести до стануIterators


По-перше, знайте, що Iterableце підручне зображення Traversable.

По-друге,

  • Traversableвимагає впровадження foreachметоду, який використовується всім іншим.

  • Iterableвимагає впровадження iteratorметоду, який використовується всім іншим.

Наприклад, реалізація findдля Traversableвикористання foreach(через розуміння) та викид для 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надається спосіб призупинити, відновити чи зупинити ітерацію за допомогою стану Iterator. З Traversable, це все або нічого (не допускає винятків для контролю потоку).

Більшу частину часу це не має значення, і вам потрібно більш загальний інтерфейс. Але якщо вам коли-небудь потрібен більш налаштований контроль над ітерацією, вам знадобиться документ Iterator, який ви можете отримати з Iterable.


1

Відповідь Даніеля звучить добре. Дозвольте мені побачити, чи можу я сказати це своїми словами.

Таким чином, Ітерабельний може дати вам ітератор, який дозволяє вам перетинати елементи один за одним (використовуючи next ()), а також зупинятися та їхати як завгодно. Для цього ітератору необхідно зберегти внутрішній «покажчик» на позицію елемента. Але Траверсай дає вам метод, передбачити, щоб пройти всі елементи одразу, не зупиняючись.

Щось на зразок діапазону (1, 10) повинно бути лише 2 цілих числа, як стан як прохідний. Але Діапазон (1, 10) як Ітерабельний дає вам ітератор, який повинен використовувати 3 цілі числа для стану, одне з яких - індекс.

Зважаючи на те, що Traversable також пропонує foldLeft, foldRight, його передбачати потрібно, щоб перемістити елементи у відомому та фіксованому порядку. Тому можливо реалізувати ітератор для прохідного каналу. Напр. Def iterator = toList.iterator

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.