Файл: Одерски Мартин, Спун Лекс, Веннерс Билл, Соммерс ФрэнкО41 Scala. Профессиональное программирование. 5е изд спб. Питер, 2022. 608 с. ил. Серия Библиотека программиста.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 09.12.2023
Просмотров: 766
Скачиваний: 11
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
64 Глава 2 • Первые шаги в Scala
Шаг 5 . Организуем цикл с while и принимаем решение с if
Чтобы попробовать в работе конструкцию while
, наберите следующий код и сохраните его в файле printargs.scala
:
@main def m(args: String*) =
var i = 0
while i < args.length do println(args(i))
i += 1
ПРИМЕЧАНИЕ
Хотя примеры в данном разделе помогают объяснить суть циклов while, они не демонстрируют наилучший стиль программирования на Scala . В следу- ющем разделе будут показаны более рациональные подходы, позволяющие избежать повторения последовательностей с помощью индексов .
Этот скрипт начинается с определения переменой, var i
=
0
. Вывод типов относит переменную i
к типу
Int
, поскольку это тип ее начального значе
ния
0
. Конструкция while на следующей строке заставляет блок (две строки кода снизу) повторно выполняться, пока булево выражение i
<
args.length будет вычисляться в false
. Метод args.length вычисляет длину после
довательности args
. Блок содержит две инструкции, каждая из которых набрана с отступом в два пробела, что является рекомендуемым стилем от
ступов для кода на Scala. Первая инструкция, println(args(i))
, выводит на экран i
й аргумент командной строки. Вторая, i
+=
1
, увеличивает значение переменной i
на единицу. Обратите внимание: Javaкод
++i и i++
в Scala не работает. Чтобы в Scala увеличить значение переменной на единицу, нужно использовать одно из двух выражений: либо i
=
i
+
1
, либо i
+=
1
. Запустите этот скрипт с помощью команды, показанной ниже:
$ scala printargs.scala Scala is fun
И вы увидите:
Scala is fun
Далее наберите в новом файле по имени echoargs.scala следующий код:
@main def m(args: String*) =
var i = 0
Шаг 5 . Организуем цикл с while и принимаем решение с if 65
while i < args.length do if i != 0 then print(" ")
print(args(i))
i += 1
println()
В целях вывода всех аргументов в одной и той же строке в этой версии вме
сто вызова println используется вызов print
. Чтобы эту строку можно было прочитать, перед каждым аргументом, за исключением первого, благодаря использованию конструкции if i
!=
0
then вставляется пробел. При первом проходе цикла while выражение i
!=
0
станет вычисляться в false
, поэтому перед начальным элементом пробел выводиться не будет. В самом конце добавлена еще одна инструкция println
, чтобы после вывода аргументов произошел переход на новую строку. Тогда у вас получится очень красивая картинка. Если запустить этот скрипт с помощью команды:
$ scala echoargs.scala Scala is even more fun то вы увидите на экране такой текст:
Scala is even more fun
Обратите внимание, что в Scala, в отличие от Java, вам не нужно помещать логическое выражение while или if в круглые скобки. Еще одно отличие от
Java состоит в том, что вы можете опустить фигурные скобки в блоке, даже если он содержит более одного оператора, при условии, что вы сделаете со
ответствующий отступ для каждой строки. И хотя вы не видели ни одной точки с запятой, Scala использует их для разделения операторов, как и Java, за исключением того, что в Scala эти знаки очень часто являются необяза
тельными, что дает некоторое облегчение вашему правому мизинцу. Если бы вы были более многословны, вы могли бы написать скрипт echoargs.scala в стиле Java следующим образом:
@main def m(args: String*) = {
var i = 0;
while (i < args.length) {
if (i != 0) {
print(" ");
}
print(args(i));
i += 1;
}
println();
}
66 Глава 2 • Первые шаги в Scala
Начиная со Scala 3, вместо фигурных скобок рекомендуется использовать стиль на основе отступов, называемый «тихим синтаксисом». В Scala 3 также были добавлены маркеры окончания кода, помогающие понять, где заканчиваются более крупные области с отступом. Маркеры окончания кода состоят из ключевого слова end и следующего за ним токена спецификатора, который является либо идентификатором, либо ключевым словом. Пример показан в листинге 10.9.
Шаг 6 . Перебираем элементы с foreach и for-do
Возможно, при написании циклов while на предыдущем шаге вы даже не осознавали того, что программирование велось в императивном стиле. Обыч
но он применяется с такими языками, как Java, C++ и Python. При работе в этом стиле императивные команды в случае последовательного перебора элементов в цикле выдаются поочередно и зачастую изменяемое состояние совместно используется различными функциями. Scala позволяет програм
мировать в императивном стиле, но, узнав этот язык получше, вы, скорее всего, перейдете преимущественно на функциональный стиль. По сути, одна из основных целей этой книги — помочь освоить работу в функциональном стиле, чтобы она стала такой же комфортной, как и работа в императивном.
Одна из основных характеристик функционального языка — то, что его функции относятся к конструкциям первого класса, и это абсолютно спра
ведливо для языка Scala. Например, еще один, гораздо более лаконичный вариант вывода каждого аргумента командной строки выглядит так:
@main def m(args: String*) =
args.foreach(arg => println(arg))
В этом коде в отношении массива args вызывается метод foreach
, в который передается функция. В данном случае передается функциональный литерал
с одним параметром arg
. Тело функции — вызов println(arg)
. Если набрать показанный ранее код в новом файле по имени pa.scala и запустить этот файл на выполнение с помощью команды:
$ scala pa.scala Concise is nice то на экране появятся строки:
Concise is nice
Шаг 6 . Перебираем элементы с foreach и for-do 67
В предыдущем примере компилятор Scala вывел тип arg
, причислив эту переменную к
String
, поскольку
String
— тип элемента последовательно
сти, в отношении которого вызван метод foreach
. Если вы предпочитаете конкретизировать, то можете упомянуть название типа. Но, пойдя по этому пути, придется часть кода, в которой указывается переменная аргумента, заключать в круглые скобки (это и есть обычный синтаксис):
@main def m(args: String*) =
args.foreach((arg: String) => println(arg))
При запуске этот скрипт ведет себя точно так же, как и предыдущий.
Если же вы склонны не к конкретизации, а к более лаконичному изложению кода, то можете воспользоваться специальными сокращениями, принятыми в Scala. Если функциональный литерал функции состоит из одной инструк
ции, принимающей один аргумент, то обозначать данный аргумент явным образом по имени не нужно
1
. Поэтому работать будет и следующий код:
@main def m(args: String*) =
args.foreach(println)
Резюмируем усвоенное: синтаксис для функционального литерала пред
ставляет собой список поименованных параметров, заключенный в круглые скобки, а также правую стрелку, за которой следует тело функции. Этот синтаксис показан на рис. 2.2.
Рис. 2.2. Синтаксис функционального литерала в Scala
Теперь вы можете поинтересоваться: что же случилось с теми проверенными циклами for
, которые вы привыкли использовать в таких императивных язы
ках, как Java или Python? Придерживаться функционального направления в Scala возможно с помощью только одного функционального родственни
ка императивной конструкции for
, который называется выражением for.
1
Это сокращение, которое называется частично применяемой функцией, описано в разделе 8.6.
68 Глава 2 • Первые шаги в Scala
Поскольку вы не сможете понять всю его эффективность и выразитель
ность, пока не доберетесь до раздела 7.3 (или не заглянете в него), здесь о нем будет дано лишь общее представление. Наберите в новом файле по имени forargs.scala следующий код:
@main def m(args: String*) =
for arg <- args do println(arg)
Между for и do находится arg
<- args
1
. Справа от символа
<–
расположена уже знакомая вам последовательность args
. Слева от
<–
указана переменная arg
, относящаяся к val
, а не к var
переменным (так как она всегда относится к val
переменным, записывается только arg
, а не val arg
). Может показаться, что arg относится к var
переменной, поскольку она будет получать новое значение при каждой итерации, однако в действительности она относится к val
переменной: arg не может получить новое значение внутри тела выра
жения. Вместо этого для каждого элемента массива args будет создана новая val
переменная по имени arg
, которая будет инициализирована значением элемента, и тело for будет выполнено.
Если скрипт forargs.scala запустить с помощью команды:
$ scala forargs.scala for arg in args то вы увидите:
for arg in args
Диапазон применения выражения for значительно шире, но для начала это
го примера достаточно. Мы расскажем вам больше о for в шаге 12 главы 3 и в разделе 7.3.
Резюме
В данной главе мы привели основную информацию о Scala. Надеемся, вы воспользовались возможностью создать код на этом языке. В следующей главе мы продолжим вводный обзор и рассмотрим более сложные темы.
1
Вы можете интерпретировать символ
<–
как in
. Следовательно, выражение for arg
<–
args do можно прочитать как for arg in args do
3
Дальнейшие шаги в Scala
В этой главе продолжается введение в Scala, начатое в предыдущей главе.
Здесь мы рассмотрим более сложные функциональные возможности. Когда вы усвоите материал главы, у вас будет достаточно знаний, чтобы начать создавать полезные скрипты на Scala. Мы вновь рекомендуем по мере чтения текста получать практические навыки с помощью приводимых примеров.
Лучше всего осваивать Scala, начиная создавать код на данном языке.
Шаг 7 . Параметризуем массивы типами
В Scala создавать объекты или экземпляры класса можно с помощью клю
чевого слова new
. При создании объекта в Scala вы можете параметризовать его значениями и типами. Параметризация означает «конфигурирование» экземпляра при его создании. Параметризация экземпляра значениями произ
водится путем передачи конструктору объектов в круглых скобках. Например, код Scala, который показан ниже, создает новый объект java.math.BigInteger
, выполняя его параметризацию значением "12345"
:
val big = new java.math.BigInteger("12345")
Параметризация экземпляра типами выполняется с помощью указания одного или нескольких типов в квадратных скобках. Пример показан в ли
стинге 3.1. Здесь greetStrings
— значение типа
Array[String]
(«массив строк»), инициализируемое длиной
3
путем его параметризации значе
нием
3
в первой строке кода. Если запустить код в листинге 3.1 в качестве скрипта, то вы увидите еще одно приветствие
Hello,
world!
. Учтите, что при параметризации экземпляра как типом, так и значением тип стоит
70 Глава 3 • Дальнейшие шаги в Scala первым и указывается в квадратных скобках, а за ним следует значение в круглых скобках.
Листинг 3.1. Параметризация массива типом val greetStrings = new Array[String](3)
greetStrings(0) = "Hello"
greetStrings(1) = ", "
greetStrings(2) = "world!\n"
for i <- 0 to 2 do print(greetStrings(i))
ПРИМЕЧАНИЕ
Хотя код в листинге 3 .1 содержит важные понятия, он не показывает рекомендуемый способ создания и инициализации массива в Scala . Более рациональный способ будет показан в листинге 3 .2 .
Если вы склонны делать более явные указания, то тип greetStrings можно обозначить так:
val greetStrings: Array[String] = new Array[String](3)
С учетом имеющегося в Scala вывода типов эта строка кода семантически эквивалентна первой строке листинга 3.1. Но в данной форме показано сле
дующее: часть параметризации, которая относится к типу (название типа в квадратных скобках), формирует часть типа экземпляра, однако часть параметризации, относящаяся к значению (значения в круглых скобках), в формировании не участвует. Типом greetStrings является
Array[String]
, а не
Array[String](3)
В следующих трех строках кода в листинге 3.1 инициализируется каждый элемент массива greetStrings
:
greetStrings(0) = "Hello"
greetStrings(1) = ", "
greetStrings(2) = "world!\n"
Как уже упоминалось, доступ к массивам в Scala осуществляется за счет по
мещения индекса элемента в круглые, а не в квадратные скобки, как в Java.
Следовательно, нулевым элементом массива будет greetStrings(0)
, а не greetStrings[0]
Эти три строки кода иллюстрируют важное понятие, помогающее осмыс
лить значение для Scala val
переменных. Когда переменная определяется
Шаг 7 . Параметризуем массивы типами 71
с помощью val
, повторно присвоить значение данной переменной нельзя, но объект, на который она ссылается, потенциально может быть изменен.
Следовательно, в данном случае присвоить greetStrings значение другого массива невозможно — переменная greetStrings всегда будет указывать на один и тот же экземпляр типа
Array[String]
, которым она была инициали
зирована. Но впоследствии в элементы типа
Array[String]
можно вносить изменения, то есть сам массив является изменяемым.
Последние две строки листинга 3.1 содержат выражение for
, которое по
очередно выводит каждый элемент массива greetStrings
:
for i <- 0 to 2 do print(greetStrings(i))
В первой строке кода для этого выражения for показано еще одно общее пра
вило Scala: если метод получает лишь один параметр, то его можно вызвать без точки или круглых скобок. В данном примере to на самом деле является методом, получающим один
Int
аргумент. Код
0
to
2
преобразуется в вызов метода
0.to(2)
1
. Следует заметить, что этот синтаксис работает только при явном указании получателя вызова метода. Код println
10
использовать нельзя, а код
Console println
10
— можно.
С технической точки зрения в Scala нет перегрузки операторов, поскольку в нем фактически отсутствуют операторы в традиционном понимании.
Вместо этого такие символы, как
+
,
-
,
*
,
/
, могут использоваться в качестве имен методов. Следовательно, когда при выполнении шага 1 вы набираете в интерпретаторе Scala код
1
+
2
, в действительности вы вызываете метод по имени
+
в отношении
Int
объекта
1
, передавая ему в качестве параметра значение
2
. Как показано на рис. 3.1, вместо этого
1
+
2
можно записать с по
мощью традиционного синтаксиса вызова метода:
1.+(2)
Еще одна весьма важная идея, проиллюстрированная в этом примере, по
может понять, почему доступ к элементам массивов Scala осуществляется с помощью круглых скобок. В Scala меньше особых случаев по сравнению с Java. Массивы в Scala, как и в случае с любыми другими классами, — просто экземпляры классов. При использовании круглых скобок, окру
жающих одно или несколько значений переменной, Scala преобразует код в вызов метода по имени apply применительно к данной переменной.
1
Этот метод to фактически возвращает не массив, а иную разновидность последо
вательности, содержащую значения 0, 1 и 2, последовательный перебор которых выполняется выражением for
. Последовательности и другие коллекции будут рассматриваться в главе 15.
1 ... 4 5 6 7 8 9 10 11 ... 64