Scala for Backend Engineering & Functional Programming · Урок

Бесконечные потоки

Безопасно моделируйте бесконечные данные.

Урок 3 из 413 шагов

«Бесконечные потоки» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Scala for Backend Engineering & Functional Programming, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.

Безопасная бесконечность

LazyList может описывать бесконечную последовательность, поскольку его хвост не вычисляется, пока это не потребуется. Вы материализуете только начало, которое потребляете.

Это позволяет моделировать натуральные числа, простые числа или показания датчиков, не задавая ограничение заранее.

Все натуральные числа

LazyList.from(1) — бесконечная последовательность 1, 2, 3, ... Вы можете взять любой конечный префикс.

Принудительное вычисление всей последовательности никогда не завершится, поэтому всегда ограничивайте её с помощью take или останавливайте предикатом.

object Demo extends App {
  val nats = LazyList.from(1)
  println(nats.take(5).toList)
}

from с шагом

LazyList.from(start, step) считает с заданным интервалом. Используйте его для чётных чисел, тактов или любой арифметической прогрессии.

Последовательность бесконечна, но каждый вызов take вычисляет только то, что Вы запросили.

object Demo extends App {
  val evens = LazyList.from(0, 2)
  println(evens.take(5).toList)
}

Самоссылочные потоки

Известный приём: определить LazyList через него самого. Последовательность Фибоначчи можно записать, объединив поток с его собственным хвостом.

Это работает только потому, что хвост остаётся невычисленным, пока не будет получена очередная ячейка.

lazy val fibs: LazyList[Int] =
  0 #:: 1 #:: fibs.zip(fibs.tail).map { case (a, b) => a + b }
// fibs(0)=0, fibs(1)=1, fibs(2)=1 ...

Запуск Фибоначчи

Принудительно вычислим начало самоссылочного потока Фибоначчи.

Каждый новый элемент вычисляется на основе более ранних, уже сохранённых элементов, поэтому при получении новых значений вся последовательность остаётся эффективной.

object Demo extends App {
  lazy val fibs: LazyList[Int] =
    0 #:: 1 #:: fibs.zip(fibs.tail).map { case (a, b) => a + b }
  println(fibs.take(10).toList)
}

iterate для последовательностей

LazyList.iterate создаёт бесконечную последовательность с помощью функции перехода. Степени, геометрический рост и конечные автоматы естественно выражаются таким способом.

Здесь каждый элемент втрое больше предыдущего — и так бесконечно.

object Demo extends App {
  val triples = LazyList.iterate(1)(_ * 3)
  println(triples.take(6).toList)
}

Решето простых чисел

Бесконечные потоки особенно хорошо подходят для решета Эратосфена. Возьмите простое число в голове, отфильтруйте его кратные из остальной части и повторите процесс.

Фильтрация выполняется лениво, поэтому простые числа создаются по одному по мере потребления.

def sieve(s: LazyList[Int]): LazyList[Int] =
  s.head #:: sieve(s.tail.filter(_ % s.head != 0))

val primes = sieve(LazyList.from(2))

Запуск решета

Теперь получим первые десять простых чисел из этого бесконечного решета.

Принудительно вычисляется только такой объём исходного потока чисел, которого достаточно для получения десяти простых чисел. Это демонстрирует вычисления по требованию.

object Demo extends App {
  def sieve(s: LazyList[Int]): LazyList[Int] =
    s.head #:: sieve(s.tail.filter(_ % s.head != 0))
  val primes = sieve(LazyList.from(2))
  println(primes.take(10).toList)
}

Никогда не вычисляйте всё целиком

Методы, которым нужна вся последовательность, например length, toList для неограниченного потока или foreach без остановки, зависнут на бесконечном LazyList.

Сначала всегда ограничивайте последовательность с помощью take, takeWhile или find.

// DON'T: LazyList.from(1).toList    // hangs forever
val ok = LazyList.from(1).take(3).toList

Ловушка удержания головы

Если val хранит голову бесконечного мемоизирующего LazyList и Вы потребляете последовательность далеко вперёд, каждая вычисленная ячейка остаётся в памяти, вызывая утечку памяти.

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

Зачем нужны бесконечные потоки

Бесконечные потоки позволяют отделить создание данных от их потребления. Производитель описывает бесконечное правило, а потребитель решает, какой объём материализовать.

Это мощный функциональный шаблон для конвейеров, имитаций и ленивых источников данных.

Быстрая проверка

Проверьте своё понимание бесконечных LazyLists.

Итоги

Бесконечные LazyLists работают благодаря тому, что хвост вычисляется по требованию: from, iterate, самоссылочный fibs и решето Эратосфена генерируют элементы бесконечно, но создают только те из них, которые Вы потребляете.

Избегайте операций над всей последовательностью и удержания головы. Далее Вы научитесь лениво брать элементы и фильтровать их.

Можно начать бесплатно

Изучай Scala с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
39
Уроки
143

Часто задаваемые вопросы

Урок «Бесконечные потоки» бесплатный?

Да — полный текст урока «Бесконечные потоки» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.

Чему я научусь в уроке «Бесконечные потоки»?

Безопасно моделируйте бесконечные данные. Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?

Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Бесконечные потоки»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?

Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Объяснение ленивых вычислений
  2. Создание LazyList
  3. Бесконечные потоки
  4. Ленивое извлечение и фильтрация
← Назад к Scala for Backend Engineering & Functional Programming