Ключ к пониманию этого поведения, я думаю, заключается в подписи foldRight
.
def foldRight[B](z: => B)(f: (A, => B) => B): B = ...
Обратите внимание, что второй аргумент, f
, является функцией, которая принимает два параметра: A
и псевдоним (ленивый) B
. Уберите эту лень, f: (A, B) => B
, и вы не только получите ожидаемую группировку методов (все шаги map()
перед всеми filter()
шагами), они также идут в обратном порядке: сначала обрабатывается 6
, а 1
обрабатывается последним, как и следовало ожидать от foldRight
.
Как один маленький =>
выполняет всю эту магию? В основном это говорит о том, что 2-й аргумент f()
будет храниться в резерве, пока он не потребуется.
Итак, пытаясь ответить на ваши вопросы.
- Почему чередуются выходные данные карты и фильтра?
Поскольку каждый вызов map()
и filter()
задерживается до момента, когда запрашиваются значения.
- Не могли бы вы объяснить все этапы создания потока до последнего шага для получения этого поведения?
Не совсем. Это заняло бы больше времени и ТАКОГО места для ответов, чем я готов внести, но давайте сделаем всего несколько шагов в болоте.
Мы начинаем с Stream
, который выглядит как серия Cons
, каждая из которых содержит Int
и ссылку на следующий Cons
, но это не совсем точно. Каждый Cons
действительно содержит две функции, при вызове 1-й производит Int
, а 2-й производит следующий Cons
.
Позвоните map()
и передайте ему функцию "+10". map()
создает новую функцию: «Учитывая h
и t
(оба значения), создайте new Cons
. Функция заголовка нового Cons
при вызове будет функция "+10" применяется к текущему значению напора. Новая функция хвоста будет выдавать значение t
в полученном виде. " Эта новая функция передается в foldRight
.
foldRight
получает новую функцию, но оценка второго параметра функции будет отложена до тех пор, пока она не потребуется. h()
вызывается для получения текущего значения заголовка, t()
вызывается для извлечения текущего значения хвоста, и для него вызывается рекурсивный вызов foldRight
.
Вызовите filter()
и передайте ему функцию isEven. filter()
создает новую функцию: «Дано h
и t
, создайте new Cons
, если h
пройдет тест isEven. Если нет, верните t
«. Это настоящий t
. Не обещаю оценить его значение позже.
- Где находятся другие элементы списка, которые также проходят преобразование фильтра, так что 4 и 6?
Они все еще там, ожидая оценки. Мы можем форсировать эту оценку, используя сопоставление с образцом для извлечения различных Cons
один за другим.
val c0@Cons(_,_) = Stream(1,2,3,4,5,6).map(_+10).filter(_%2==0)
// **STDOUT**
//map h:1
//filter h:11
//map h:2
//filter h:12
c0.h() //res0: Int = 12
val c1@Cons(_,_) = c0.t()
// **STDOUT**
//map h:3
//filter h:13
//map h:4
//filter h:14
c1.h() //res1: Int = 14
val c2@Cons(_,_) = c1.t()
// **STDOUT**
//map h:5
//filter h:15
//map h:6
//filter h:16
c2.h() //res2: Int = 16
c2.t() //res3: Stream[Int] = Empty