Scala предлагает простую запись для выражения последовательных преобразований. Эти преобразования можно упростить используя специальный синтаксис for выражения
(for comprehension), который записывается как for (enumerators) yield e
, где enumerators
относятся к списку перечислителей, разделенных точкой с запятой. Где отдельный такой “перечислитель” (enumerator) является либо генератором, который вводит новые переменные, либо фильтром. For-выражение вычисляет тело e
(которое связанно с тем что генерирует enumerator) и возвращает последовательность вычислений.
Вот пример:
case class User(name: String, age: Int)
val userBase = List(
User("Travis", 28),
User("Kelly", 33),
User("Jennifer", 44),
User("Dennis", 23))
val twentySomethings =
for (user <- userBase if user.age >=20 && user.age < 30)
yield user.name // т. е. добавить результат к списку
twentySomethings.foreach(println) // выводит "Travis Dennis"
case class User(name: String, age: Int)
val userBase = List(
User("Travis", 28),
User("Kelly", 33),
User("Jennifer", 44),
User("Dennis", 23))
val twentySomethings =
for user <- userBase if user.age >=20 && user.age < 30
yield user.name // т. е. добавить результат к списку
twentySomethings.foreach(println) // выводит "Travis Dennis"
for
-выражение, используется с оператором yield
, на самом деле создает List
. Потому что мы указали yield user.name
(то есть вывести имя пользователя), получаем List[String]
. user <- userBase
и есть наш генератор, а if (user.age >=20 && user.age < 30)
- это фильтр который отфильтровывает пользователей, не достигших 30-летнего возраста.
Ниже приведен более сложный пример использования двух генераторов. Он вычисляет все пары чисел между 0
и n-1
, сумма которых равна заданному значению v
:
def foo(n: Int, v: Int) =
for (i <- 0 until n;
j <- 0 until n if i + j == v)
yield (i, j)
foo(10, 10).foreach {
case (i, j) =>
println(s"($i, $j) ") // выводит (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) (6, 4) (7, 3) (8, 2) (9, 1)
}
def foo(n: Int, v: Int) =
for i <- 0 until n
j <- 0 until n if i + j == v
yield (i, j)
foo(10, 10).foreach {
(i, j) => println(s"($i, $j) ") // выводит (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) (6, 4) (7, 3) (8, 2) (9, 1)
}
Здесь n == 10
и v == 10
. На первой итерации i == 0
и j == 0
так i + j != v
и поэтому ничего не выдается. j
увеличивается еще в 9 раз, прежде чем i
увеличивается до 1
. Без фильтра if
будет просто напечатано следующее:
(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 0) ...
Обратите внимание, что for-выражение не ограничивается только работой со списками. Каждый тип данных, поддерживающий операции withFilter
, map
, and flatMap
(с соответствующими типами), может быть использован в for-выражении.
Вы можете обойтись без yield
в for-выражении. В таком случае, результатом будет Unit
. Это может быть полезным для выполнения кода основанного на побочных эффектах. Вот программа, эквивалентная предыдущей, но без использования yield
:
def foo(n: Int, v: Int) =
for (i <- 0 until n;
j <- 0 until n if i + j == v)
println(s"($i, $j)")
foo(10, 10)
def foo(n: Int, v: Int) =
for i <- 0 until n
j <- 0 until n if i + j == v
do println(s"($i, $j)")
foo(10, 10)
Дополнительные ресурсы
- Другие примеры “For comprehension” доступны в книге Scala
Contributors to this page:
Contents
- Введение
- Основы
- Единобразие типов
- Классы
- Значения Параметров По умолчанию
- Именованные Аргументы
- Трейты
- Кортежи
- Композиция классов с трейтами
- Функции Высшего Порядка
- Вложенные Методы
- Множественные списки параметров (Каррирование)
- Классы Образцы
- Сопоставление с примером
- Объекты Одиночки
- Регулярные Выражения
- Объект Экстрактор
- Сложные for-выражения
- Обобщенные Классы
- Вариантность
- Верхнее Ограничение Типа
- Нижнее Ограничение Типа
- Внутренние классы
- Члены Абстрактного Типа
- Составные Типы
- Самоописываемые типы
- Контекстные параметры, также известные, как неявные параметры
- Неявные Преобразования
- Полиморфные методы
- Выведение Типа
- Операторы
- Вызов по имени
- Аннотации
- Пакеты и Импорт
- Объекты Пакета