В Scala есть все структуры управления, которые вы найдете в других языках программирования,
а также мощные for
и match
выражения:
if
/else
for
циклы и выраженияmatch
выраженияwhile
циклыtry
/catch
Эти структуры демонстрируются в следующих примерах.
if
/else
В Scala структура управления if
/else
похожа на аналогичные структуры в других языках.
if (x < 0) {
println("negative")
} else if (x == 0) {
println("zero")
} else {
println("positive")
}
if x < 0 then
println("negative")
else if x == 0 then
println("zero")
else
println("positive")
Обратите внимание, что это действительно выражение, а не утверждение. Это означает, что оно возвращает значение, поэтому вы можете присвоить результат переменной:
val x = if (a < b) { a } else { b }
val x = if a < b then a else b
Как вы увидите в этой книге, все управляющие структуры Scala могут использоваться как выражения.
Выражение возвращает результат, а утверждение — нет. Утверждения обычно используются для их побочных эффектов, таких как использование
println
для печати на консоли.
for
циклы и выражения
Ключевое слово for
используется для создания цикла for
.
В этом примере показано, как напечатать каждый элемент в List
:
val ints = List(1, 2, 3, 4, 5)
for (i <- ints) println(i)
Код
i <- ints
называется генератором, а код, следующий за закрывающими скобками генератора, является телом цикла.
val ints = List(1, 2, 3, 4, 5)
for i <- ints do println(i)
Код
i <- ints
называется генератором, а код, следующий за ключевым словомdo
, является телом цикла.
Guards
Вы также можете использовать одно или несколько if
выражений внутри цикла for
.
Их называют ограничители (guards).
В этом примере выводятся все числа ints
, большие 2
:
for (i <- ints if i > 2)
println(i)
for
i <- ints
if i > 2
do
println(i)
Вы можете использовать несколько генераторов и стражников.
Этот цикл перебирает числа от 1
до 3
, и для каждого числа также перебирает символы от a
до c
.
Однако у него также есть два стражника, поэтому оператор печати вызывается только тогда,
когда i
имеет значение 2
и j
является символом b
:
for {
i <- 1 to 3
j <- 'a' to 'c'
if i == 2
if j == 'b'
} {
println(s"i = $i, j = $j") // печатает: "i = 2, j = b"
}
for
i <- 1 to 3
j <- 'a' to 'c'
if i == 2
if j == 'b'
do
println(s"i = $i, j = $j") // печатает: "i = 2, j = b"
Выражения for
Ключевое слово for
содержит в себе еще большую силу:
когда вы используете ключевое слово yield
вместо do
, то создаете выражения for
,
которые используются для вычислений и получения результатов.
Несколько примеров демонстрируют это.
Используя тот же список ints
, что и в предыдущем примере, этот код создает новый список,
в котором значение каждого элемента в новом списке в два раза превышает значение элементов в исходном:
scala> val doubles = for (i <- ints) yield i * 2
val doubles: List[Int] = List(2, 4, 6, 8, 10)
scala> val doubles = for i <- ints yield i * 2
val doubles: List[Int] = List(2, 4, 6, 8, 10)
Синтаксис структуры управления Scala является гибким,
и это for
выражение может быть записано несколькими другими способами, в зависимости от ваших предпочтений:
val doubles = for (i <- ints) yield i * 2
val doubles = for (i <- ints) yield (i * 2)
val doubles = for { i <- ints } yield (i * 2)
val doubles = for i <- ints yield i * 2 // стиль показан выше
val doubles = for (i <- ints) yield i * 2
val doubles = for (i <- ints) yield (i * 2)
val doubles = for { i <- ints } yield (i * 2)
В этом примере показано, как сделать первый символ в каждой строке списка заглавными:
val names = List("chris", "ed", "maurice")
val capNames = for (name <- names) yield name.capitalize
val names = List("chris", "ed", "maurice")
val capNames = for name <- names yield name.capitalize
Наконец, нижеследующее выражение for
перебирает список строк
и возвращает длину каждой строки, но только если эта длина больше 4
:
val fruits = List("apple", "banana", "lime", "orange")
val fruitLengths =
for (f <- fruits if f.length > 4) yield f.length
// fruitLengths: List[Int] = List(5, 6, 6)
val fruits = List("apple", "banana", "lime", "orange")
val fruitLengths = for
f <- fruits
if f.length > 4
yield
// здесь можно использовать
// несколько строк кода
f.length
// fruitLengths: List[Int] = List(5, 6, 6)
for
циклы и выражения более подробно рассматриваются в разделах этой книги “Структуры управления”
и в справочной документации.
match
выражения
В Scala есть выражение match
, которое в своем самом простом использовании похоже на switch
оператор Java:
val i = 1
// позже в этом коде ...
i match {
case 1 => println("one")
case 2 => println("two")
case _ => println("other")
}
val i = 1
// позже в этом коде ...
i match
case 1 => println("one")
case 2 => println("two")
case _ => println("other")
Однако match
на самом деле это выражение, означающее,
что оно возвращает результат на основе совпадения с шаблоном, который вы можете привязать к переменной:
val result = i match {
case 1 => "one"
case 2 => "two"
case _ => "other"
}
val result = i match
case 1 => "one"
case 2 => "two"
case _ => "other"
match
не ограничивается работой только с целочисленными значениями, его можно использовать с любым типом данных:
val p = Person("Fred")
// позже в этом коде ...
p match {
case Person(name) if name == "Fred" =>
println(s"$name says, Yubba dubba doo")
case Person(name) if name == "Bam Bam" =>
println(s"$name says, Bam bam!")
case _ => println("Watch the Flintstones!")
}
val p = Person("Fred")
// позже в этом коде ...
p match
case Person(name) if name == "Fred" =>
println(s"$name says, Yubba dubba doo")
case Person(name) if name == "Bam Bam" =>
println(s"$name says, Bam bam!")
case _ => println("Watch the Flintstones!")
На самом деле match
выражение можно использовать для проверки переменной на множестве различных типов шаблонов.
В этом примере показано (а) как использовать match
выражение в качестве тела метода и (б) как сопоставить все показанные различные типы:
// getClassAsString - метод, принимающий один параметр любого типа.
def getClassAsString(x: Any): String = x match {
case s: String => s"'$s' is a String"
case i: Int => "Int"
case d: Double => "Double"
case l: List[_] => "List"
case _ => "Unknown"
}
// примеры
getClassAsString(1) // Int
getClassAsString("hello") // 'hello' is a String
getClassAsString(List(1, 2, 3)) // List
Поскольку метод getClassAsString
принимает значение параметра типа Any
, его можно разложить по любому шаблону.
// getClassAsString - метод, принимающий один параметр любого типа.
def getClassAsString(x: Matchable): String = x match
case s: String => s"'$s' is a String"
case i: Int => "Int"
case d: Double => "Double"
case l: List[?] => "List"
case _ => "Unknown"
// примеры
getClassAsString(1) // Int
getClassAsString("hello") // 'hello' is a String
getClassAsString(List(1, 2, 3)) // List
Метод getClassAsString
принимает в качестве параметра значение типа Matchable,
которое может быть любым типом, поддерживающим сопоставление с образцом
(некоторые типы не поддерживают сопоставление с образцом, поскольку это может нарушить инкапсуляцию).
Сопоставление с образцом в Scala гораздо шире. Шаблоны могут быть вложены друг в друга, результаты шаблонов могут быть связаны, а сопоставление шаблонов может даже определяться пользователем. Дополнительные сведения см. в примерах сопоставления с образцом в главе “Структуры управления”.
try
/catch
/finally
Структура управления Scala try
/catch
/finally
позволяет перехватывать исключения.
Она похожа на аналогичную структуру в Java, но её синтаксис соответствует match
выражениям:
try {
writeTextToFile(text)
} catch {
case ioe: IOException => println("Got an IOException.")
case nfe: NumberFormatException => println("Got a NumberFormatException.")
} finally {
println("Clean up your resources here.")
}
try
writeTextToFile(text)
catch
case ioe: IOException => println("Got an IOException.")
case nfe: NumberFormatException => println("Got a NumberFormatException.")
finally
println("Clean up your resources here.")
Циклы while
В Scala также есть конструкция цикла while
.
Его однострочный синтаксис выглядит так:
while (x >= 0) { x = f(x) }
while x >= 0 do x = f(x)
Scala 3 по-прежнему поддерживает синтаксис Scala 2 для обратной совместимости.
Синтаксис while
многострочного цикла выглядит следующим образом:
var x = 1
while (x < 3) {
println(x)
x += 1
}
var x = 1
while
x < 3
do
println(x)
x += 1
Пользовательские структуры управления
Благодаря таким функциям, как параметры по имени, инфиксная нотация, плавные интерфейсы, необязательные круглые скобки, методы расширения и функции высшего порядка, вы также можете создавать свой собственный код, который работает так же, как управляющая структура. Вы узнаете об этом больше в разделе “Структуры управления”.
Contributors to this page:
Contents
- Введение
- Возможности Scala
- Почему Scala 3?
- Почувствуй Scala
- Пример 'Hello, World!'
- REPL
- Переменные и типы данных
- Структуры управления
- Моделирование данных
- Методы
- Функции первого класса
- Одноэлементные объекты
- Коллекции
- Контекстные абстракции
- Верхнеуровневые определения
- Обзор
- Первый взгляд на типы
- Интерполяция строк
- Структуры управления
- Моделирование предметной области
- Инструменты
- Моделирование ООП
- Моделирование ФП
- Методы
- Особенности методов
- Main методы в Scala 3
- Обзор
- Функции
- Анонимные функции
- Параметры функции
- Eta расширение
- Функции высшего порядка
- Собственный map
- Создание метода, возвращающего функцию
- Обзор
- Пакеты и импорт
- Коллекции в Scala
- Типы коллекций
- Методы в коллекциях
- Обзор
- Функциональное программирование
- Что такое функциональное программирование?
- Неизменяемые значения
- Чистые функции
- Функции — это значения
- Функциональная обработка ошибок
- Обзор
- Типы и система типов
- Определение типов
- Параметризованные типы
- Пересечение типов
- Объединение типов
- Алгебраические типы данных
- Вариантность
- Непрозрачные типы
- Структурные типы
- Зависимые типы функций
- Другие типы
- Контекстные абстракции
- Методы расширения
- Параметры контекста
- Контекстные границы
- Given импорты
- Классы типов
- Многостороннее равенство
- Неявное преобразование типов
- Обзор
- Параллелизм
- Scala утилиты
- Сборка и тестирование проектов Scala с помощью Sbt
- Рабочие листы
- Взаимодействие с Java