for

for loops

В самом простом случае цикл for в Scala можно использовать для перебора элементов в коллекции. Например, имея последовательность целых чисел, можно перебрать ее элементы и вывести значения следующим образом:

val ints = List(1, 2, 3, 4, 5)
// ints: List[Int] = List(1, 2, 3, 4, 5)
for i <- ints do println(i)
// 1
// 2
// 3
// 4
// 5

Код i <- ints называется генератором.

Если необходим многострочный блок кода после генератора for, используется следующий синтаксис:

for
  i <- ints
do
  val x = i * 2
  println(s"i = $i, x = $x")
// i = 1, x = 2
// i = 2, x = 4
// i = 3, x = 6
// i = 4, x = 8
// i = 5, x = 10

Несколько генераторов

В цикле for можно использовать несколько генераторов, например:

for
  i <- 1 to 2
  j <- 'a' to 'b'
  k <- 1 to 10 by 5
do
  println(s"i = $i, j = $j, k = $k")
// i = 1, j = a, k = 1
// i = 1, j = a, k = 6
// i = 1, j = b, k = 1
// i = 1, j = b, k = 6
// i = 2, j = a, k = 1
// i = 2, j = a, k = 6
// i = 2, j = b, k = 1
// i = 2, j = b, k = 6

Guards

Циклы for также могут содержать условия, называемые guards:

for
  i <- 1 to 5
  if i % 2 == 0
do
  println(i)
// 2
// 4

Можно добавлять столько условий, сколько необходимо:

for
  i <- 1 to 10
  if i > 3
  if i < 6
  if i % 2 == 0
do
  println(i)
// 4

Использование for с Map-ами

Циклы for можно использовать с Map-ами. Например, если есть карта ключ/значение:

val states = Map(
  "AK" -> "Alaska",
  "AL" -> "Alabama", 
  "AR" -> "Arizona"
)

Можно обойти все пары ключ/значение так:

for (abbrev, fullName) <- states do println(s"$abbrev: $fullName")
// AK: Alaska
// AL: Alabama
// AR: Arizona

Когда цикл for перебирает Map, каждая пара ключ/значение привязывается к переменным abbrev и fullName. По мере выполнения цикла переменная abbrev принимает значение текущего ключа, а переменная fullName - соответствующему ключу значению.

for expressions

В предыдущих примерах все циклы for использовались для побочных эффектов, в частности, для вывода результата в STDOUT с помощью println.

Важно знать, что for также можно использовать для выражений, возвращающих значения. Для этого for создается с ключевым словом yield вместо do и возвращаемым выражением, например:

val list =
  for
    i <- 10 to 12
  yield
    i * 2
// list: IndexedSeq[Int] = Vector(20, 22, 24)

После присваивания list содержит Vector с отображаемыми значениями. Вот как работает это выражение:

В данном случае показанное выражение for эквивалентно вызову метода map:

val list = (10 to 12).map(i => i * 2)
// list: IndexedSeq[Int] = Vector(20, 22, 24)

Выражения for можно использовать всегда, когда нужно обойти все элементы в коллекции и применить алгоритм к этим элементам для создания нового списка.

Вот пример, который показывает, как использовать блок кода после yield:

val names = List("_olivia", "_walter", "_peter")
// names: List[String] = List("_olivia", "_walter", "_peter")
val capNames = for name <- names yield
  val nameWithoutUnderscore = name.drop(1)
  val capName = nameWithoutUnderscore.capitalize
  capName
// capNames: List[String] = List("Olivia", "Walter", "Peter")

Поскольку выражение for возвращает результат, его можно использовать в качестве тела метода. Пример:

def between3and10(xs: List[Int]): List[Int] =
  for
    x <- xs
    if x >= 3
    if x <= 10
  yield x
between3and10(List(1, 3, 7, 11)) 
// res7: List[Int] = List(3, 7)

Ссылки: