Несколько генераторов
Вложенная итерация
«Несколько генераторов» — бесплатный урок 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 уроков всего.
Вложенная итерация в плоской записи
For-выражение может содержать несколько генераторов. Каждый из них — это строка с <-, которая перебирает коллекцию. Вместе они создают все комбинации — своего рода вложенный цикл, записанный в плоском виде.
object Main {
def main(args: Array[String]): Unit = {
val pairs = for {
x <- List(1, 2)
y <- List('a', 'b')
} yield (x, y)
println(pairs)
}
}Это декартово произведение
Два генератора размеров m и n создают m × n результатов — полное декартово произведение. Первый генератор образует внешний цикл, второй — внутренний.
object Main {
def main(args: Array[String]): Unit = {
val grid = for {
row <- 1 to 3
col <- 1 to 3
} yield s"r$row-c$col"
println(grid.mkString(", "))
}
}Внутренний генератор может зависеть от внешнего
Последующий генератор может использовать значения, связанные предыдущими генераторами. При обычном декартовом произведении это невозможно; именно поэтому for-выражения настолько гибки.
object Main {
def main(args: Array[String]): Unit = {
val upperTriangle = for {
i <- 1 to 4
j <- i to 4
} yield (i, j)
println(upperTriangle)
}
}Три и более генератора
Можно объединять столько генераторов, сколько нужно. Три генератора образуют тройной вложенный цикл, создавая кортежи или вычисляемые значения.
object Main {
def main(args: Array[String]): Unit = {
val triples = for {
a <- 1 to 2
b <- 1 to 2
c <- 1 to 2
} yield (a, b, c)
println(triples.size + " combinations")
triples.foreach(println)
}
}Классический пример с теоремой Пифагора
Известный пример — поиск пифагоровых троек. Мы перебираем три числа и оставляем только те, для которых выполняется a*a + b*b == c*c, сочетая несколько генераторов с защитным условием.
object Main {
def main(args: Array[String]): Unit = {
val triples = for {
a <- 1 to 20
b <- a to 20
c <- b to 20
if a * a + b * b == c * c
} yield (a, b, c)
triples.foreach(println)
}
}Как выполняется преобразование
Несколько генераторов превращаются во вложенные вызовы flatMap с завершающим map:
xs.flatMap(x => ys.flatMap(y => zs.map(z => ...)))
Каждый дополнительный генератор добавляет ещё один уровень вложенности.
object Main {
def main(args: Array[String]): Unit = {
val sugar = for {
x <- List(1, 2)
y <- List(3, 4)
} yield x * y
val desugared = List(1, 2).flatMap(x => List(3, 4).map(y => x * y))
println(sugar == desugared)
}
}Сочетание генераторов и защитных условий
Генераторы и защитные условия можно свободно чередовать. Защитное условие между двумя генераторами отбрасывает внешние значения ещё до запуска внутреннего цикла.
object Main {
def main(args: Array[String]): Unit = {
val result = for {
x <- 1 to 5
if x % 2 == 1
y <- 1 to 5
if y > x
} yield (x, y)
println(result)
}
}Объединение вложенных коллекций в одну
Если у Вас есть коллекция коллекций, два генератора естественным образом объединяют её: первый перебирает внешнюю коллекцию, второй — каждую внутреннюю.
object Main {
def main(args: Array[String]): Unit = {
val matrix = List(List(1, 2), List(3, 4), List(5, 6))
val flat = for {
row <- matrix
value <- row
} yield value
println(flat)
}
}Создание строковой таблицы
Несколько генераторов отлично подходят для создания структурированного вывода, например координат или таблицы умножения.
object Main {
def main(args: Array[String]): Unit = {
val table = for {
i <- 1 to 3
j <- 1 to 3
} yield s"$i*$j=${i * j}"
println(table.grouped(3).map(_.mkString(" ")).mkString("\n"))
}
}Сочетание коллекций разного размера
Генераторы не обязаны иметь одинаковую длину. Создаётся каждая комбинация, состоящая из одного элемента каждой коллекции.
object Main {
def main(args: Array[String]): Unit = {
val sizes = List("S", "M", "L")
val colors = List("red", "blue")
val products = for {
s <- sizes
c <- colors
} yield s"$c-$s"
println(products)
}
}Порядок влияет на последовательность результатов
Перестановка генераторов сохраняет тот же набор комбинаций, но изменяет их порядок, поскольку последний генератор изменяется быстрее всего.
object Main {
def main(args: Array[String]): Unit = {
val ab = for { x <- List(1, 2); y <- List(9, 8) } yield (x, y)
val ba = for { y <- List(9, 8); x <- List(1, 2) } yield (x, y)
println(ab)
println(ba)
}
}Быстрая проверка
Сколько кортежей создаёт for { x <- List(1,2,3); y <- List('a','b') } yield (x, y)?
Итоги
Вы узнали о нескольких генераторах:
- Каждая строка с
<-добавляет уровень вложенной итерации - Вместе они создают декартово произведение
- Внутренние генераторы могут зависеть от значений, связанных внешними генераторами
- Они преобразуются во вложенные вызовы
flatMapи завершающийmap - Порядок генераторов изменяет порядок результатов
Часто задаваемые вопросы
Урок «Несколько генераторов» бесплатный?
Да — полный текст урока «Несколько генераторов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 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 — локальная установка не требуется.
Все уроки этого курса
- Десахаризация for
- Фильтрация с помощью if
- Несколько генераторов
- Использование Option и Future