Статьи

Scala Tutorial — Scala REPL, выражения, переменные, основные типы, простые функции, сохранение и запуск программ, комментарии

Предисловие

Это первое из нескольких учебных пособий по Scala, которые я создаю для своего курса «Введение в вычислительную лингвистику» в UT Austin осенью 2011 года, в основном на основе похожих учебных пособий, которые Катрин Эрк создала для преподавания Python в аналогичном курсе . Эти учебные пособия не предполагают предшествующий опыт программирования, что, к сожалению, все еще довольно редко встречается во вселенной help-folks-learn-Scala, и которое более или менее требует создания этих учебников. Единственное исключение, о котором я знаю, это SimplyScala .

Примечание : если вы уже знаете язык программирования, этот урок, вероятно, будет не очень полезным для вас. (Хотя, возможно, некоторые из более поздних, посвященных функциональному программированию и тому подобному, которые я собираюсь сделать, будут проверены.) А пока ознакомьтесь с существующими учебными материалами по Scala, которые я перечислил на странице ссылок для курса.

В этом руководстве предполагается, что у вас установлен Scala и что вы используете некую форму Unix (если вы используете Windows, вам стоит заглянуть в Cygwin). Если у вас есть проблемы с этим, вы можете попробовать примеры, оценивая их в поле кода на SimplyScala.

(Частичный) начальный тур по выражениям, переменным и основным типам Scala

Мы будем использовать Scala REPL для ввода выражений Scala и просмотра результатов их оценки. REPL означает read-eval (uate) -print-loop, что означает, что это программа, которая (1) читает выражения, которые вы вводите, (2) оценивает их с помощью компилятора Scala, (3) распечатывает результат оценка, а затем (4) ждет вас, чтобы ввести дополнительные выражения.

Примечание : очень важно, что вы действительно вводите команды, приведенные ниже, в REPL. Если вы просто прочитаете их, во многих случаях это будет выглядеть вполне очевидно (и это так), но тот, кто плохо знаком с программированием, обычно обнаруживает много пробелов в своем понимании, фактически пробуя что-то. В частности, языки программирования очень точны, поэтому они будут делать именно то, что вы им скажете — и вы почти наверняка перепутаете некоторые вещи и извлечете уроки из этого.

В оболочке Unix введите:

1
$ scala

Вы должны увидеть что-то вроде следующего:

1
2
3
4
5
Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_26).
Type in expressions to have them evaluated.
Type :help for more information.
  
scala>

Строка scala> указывает, что REPL ожидает ввода выражений. Давайте введем некоторые.

1
2
3
4
5
scala> "Hello world"
res0: java.lang.String = Hello world
  
scala> 2
res1: Int = 2

REPL говорит нам, что первая — это строка, содержащая символы Hello world , а вторая — это Int , значением которой является целое число 2 . Строки и Ints являются типами — это простое, но важное различие, которое иногда занимает начинающее программирование, чтобы привыкнуть. Это позволяет Scala знать, что делать, когда вы хотите использовать числовое значение 2 (Int) или символ, представляющий 2 (String). Например, вот последний.

1
2
scala> "2"
res3: java.lang.String = 2

Скала знает, что разные действия допускаются разными типами. Например, Ints могут быть добавлены друг к другу.

1
2
scala> 2+3
res4: Int = 5

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

1
2
scala> "Portis" + "head"
res5: java.lang.String = Portishead

Итак, если вы считаете String «2? и строку «3?», и используйте оператор «+», вы не получите «5? — вместо этого вы получите «23?».

1
2
scala> "2" + "3"
res6: java.lang.String = 23

Мы можем попросить Scala отобразить результат оценки заданного выражения с помощью команды print .

1
2
3
4
5
6
scala> print ("Hello world")
Hello world
scala> print (2)
2
scala> print (2 + 3)
5

Обратите внимание, что результатом является действие печати, а не значение с типом. Для последнего пункта, что происходит:

  1. Scala оценивает 2 + 3, что составляет 5.
  2. Scala передает это значение в команду print .
  3. вывод на печать «5?

Вы можете представить команду print как глагол, а ее параметр (например, Hello world или 2 ) как ее объект.

Нам часто нужно сохранять результат вычисления выражения в переменной для последующего использования (на самом деле, программирование не делается без этого). Для начала давайте сделаем это тривиально, разбив приведенный выше оператор печати на два этапа.

1
2
3
4
5
scala> val x = 2 + 3
x: Int = 5
  
scala> print (x)
5

Здесь x — это переменная, которую мы указали, предварительно указав в ней значение val , которое указывает, что это фиксированная переменная, значение которой не может измениться.

Вы можете выбрать имена для переменных, но они должны следовать некоторым правилам.

  • Имена переменных могут содержать: буквы, цифры, подчеркивание
  • Они не должны начинаться с цифры
  • Они не должны быть идентичны одному из «зарезервированных слов», которые уже определены в Scala, например, для if , def , val и var.

Типичными именами переменных будут строки, такие как x , y1 , result , firstName , here_is_a_long_variable_name .

Возвращаясь к переменной x , теперь мы можем использовать ее для других вычислений.

1
2
3
4
5
scala> x + 3
res12: Int = 8
  
scala> x * x
res13: Int = 25

И, конечно, мы можем присвоить результаты таких вычислений другим переменным.

1
2
scala> val y = 3 * x + 2
y: Int = 17

Обратите внимание, что Scala знает, что умножение имеет приоритет над сложением в таких вычислениях. Если бы мы хотели переопределить это, нам нужно было бы использовать скобки, чтобы указать это, как в базовой алгебре.

1
2
scala> val z = 3 * (x + 2)
z: Int = 21

Теперь давайте введем другой тип, Double, для работы с вещественными числами. Рассмотренные до сих пор целые числа — это целые числа, которые хорошо работают с умножением, но приведут к поведению, которое вы не можете ожидать при использовании с делением.

1
2
3
4
5
scala> 5 * 2
res12: Int = 10
  
scala> 7/2
res13: Int = 3

Вы, вероятно, ожидали получить 3,5 для последнего. Тем не менее, поскольку 7 и 2 являются Ints, Scala возвращает Int — в частности, он возвращает количество раз, которое знаменатель может целиком войти в числитель. Чтобы получить результат, который вы обычно хотите получить здесь, вам нужно использовать удвоения, такие как следующие.

1
2
scala> 7.0/2.0
res14: Double = 3.5

Теперь результатом является ожидаемое вами значение типа Double. Scala использует соглашения, чтобы знать тип значений, например, он знает, что в кавычках есть строки, то есть числа, которые имеют «.» в них двойники и цифры без «.» Интс. Это важная часть того, как Scala выводит типы переменных, что является очень полезным и несколько уникальным свойством среди языков такого рода (которые называются статически типизированными языками). Чтобы увидеть это немного подробнее, обратите внимание, что вы можете явно сказать Scala, какой тип переменной.

1
2
scala> val a: Int  = 2
a: Int = 2

Часть строки a: Int указывает, что переменная a имеет тип Int. Вот несколько примеров других переменных с разными типами.

1
2
3
4
5
scala> val b: Double = 3.14
b: Double = 3.14
  
scala> val c: String = "Hello world"
c: String = Hello world

Поскольку Scala уже знает эти типы, излишне указывать их в этих случаях. Однако, когда выражения являются более сложными, иногда необходимо явно указывать типы.

Важно отметить, что мы не можем присвоить переменной тип, который конфликтует с результатом выражения. Здесь мы пытаемся присвоить значение Double переменной типа Int, и Scala сообщает об ошибке.

1
2
3
4
5
6
scala> val d: Int = 6.28
<console>:7: error: type mismatch;
found   : Double(6.28)
required: Int
val d: Int = 6.28
^

Во многих случаях, особенно с началом программирования, вам не придется беспокоиться об объявлении типов ваших переменных. Мы увидим ситуации, когда это необходимо по мере нашего прогресса.

В дополнение к переменным, объявленным с помощью val , Scala позволяет объявлять переменные с помощью var — их значения могут быть переназначены. Несколько примеров — это самый простой способ увидеть разницу.

01
02
03
04
05
06
07
08
09
10
11
12
13
scala> val a = 1
a: Int = 1
  
scala> a = 2
<console>:8: error: reassignment to val
a = 2
^
  
scala> var b = 5
b: Int = 5
  
scala> b = 6
b: Int = 6

Вы можете думать о переменной val как о герметичном стеклянном контейнере, в который вы можете посмотреть, чтобы увидеть его значение, но в котором вы не можете поместить что-то новое, и о переменной var как открываемом контейнере, который позволяет вам как видеть значение, так и менять его. новое значение для старого. Мы собираемся сосредоточиться на использовании vals в основном, поскольку они в конечном итоге дают много преимуществ в сочетании с функциональным программированием, и потому что я надеюсь, что вы начнете думать о терминах vals, а не vars, когда вы только начинаете.

функции

Переменные более полезны, когда используются в контексте функций, в которых переменная типа x может быть введена с различными значениями пользователем функции. Давайте рассмотрим преобразование градусов по Фаренгейту в градусы Цельсия. Чтобы преобразовать 87, 92 и 100 из Фаренгейта в Цельсий, мы могли бы сделать следующее.

1
2
3
4
5
6
7
8
scala> (87 - 32) * 5 / 9.0
res15: Double = 30.555555555555557
  
scala> (92 - 32) * 5 / 9.0
res16: Double = 33.333333333333336
  
scala> (100 - 32) * 5 / 9.0
res17: Double = 37.77777777777778

Очевидно, здесь много повторений. Функции позволяют нам определять общие части таких вычислений, в то время как переменные могут указывать части, которые могут отличаться. В случае преобразования единственное, что изменяется, это показание температуры в градусах Фаренгейта. Вот как мы объявляем соответствующую функцию в Scala.

1
2
scala> def f2c (x: Double) = (x - 32) * 5/9.0
f2c: (x: Double)Double

Разбивая это, мы имеем:

  • def — ключевое слово Scala, указывающее, что функция определяется
  • f2c — имя, данное функции
  • (x: Double) — параметр функции, представляющий собой переменную с именем x типа Double
  • (x — 32) * 5 / 9.0 — это тело функции, которое примет значение, данное пользователем функции, вычтет из него 32, а затем умножит результат на пять девяток

Использовать функцию просто — дайте имя функции, а затем укажите значение, которое вы передаете в функцию в скобках.

1
2
3
4
5
6
7
8
scala> f2c(87)
res18: Double = 30.555555555555557
  
scala> f2c(92)
res19: Double = 33.333333333333336
  
scala> f2c(100)
res20: Double = 37.77777777777778

И так далее. Для каждого вызова функция вычисляет выражение для x, равное значению, переданному в функцию. Теперь нам не нужно перепечатывать все обычные вещи снова и снова.

Функции могут иметь несколько аргументов. Например, следующее — это функция, которая берет два целых числа, возводит в квадрат каждое из них и затем добавляет квадратные значения.

1
2
3
4
5
scala> def squareThenAdd (x: Int, y: Int) = x*x + y*y
squareThenAdd: (x: Int, y: Int)Int
  
scala> squareThenAdd(3,4)
res21: Int = 25

Что действительно так же, как делать это явно.

1
2
scala> 3*3 + 4*4
res22: Int = 25

Важным аспектом функций является то, что все переменные должны быть связаны. Если нет, мы получаем ошибку.

1
2
3
scala> def badFunctionWithUnboundVariable (x: Int) = x + y
<console>:8: error: not found: value y
def badFunctionWithUnboundVariable (x: Int) = x + y

Функции могут делать гораздо более сложные и интересные вещи, чем то, что я показал здесь, к которому мы вернемся в другом уроке.

Редактирование программ в текстовом редакторе и запуск их в командной строке

REPL очень полезен для опробования выражений Scala и наблюдения за тем, как они оцениваются в режиме реального времени, но фактическая разработка программы выполняется путем написания текстового файла, который содержит серию выражений, которые выполняют интересное поведение. Делать это просто. Откройте текстовый редактор (см. Страницу ссылок на курс для некоторых предложений) и укажите следующую строку в качестве первой строки, ничего больше.

1
print ("Hello world")

Сохраните этот файл как HelloWorld.scala , убедившись, что он сохраняется только в виде текста. Затем в оболочке Unix перейдите в каталог, в котором сохранен этот файл, и введите следующее.

1
$ scala HelloWorld.scala

Вы увидите, что Hello world выводится, но приглашение Unix заклинило сразу после него. Вы, возможно, ожидали, что он распечатает и затем оставит приглашение Unix на следующей строке; однако в команде print или в строке, которую мы попросили напечатать, нет ничего, что указывало бы на необходимость использования новой строки. Чтобы это исправить, вернитесь в редактор и измените строку следующим образом.

1
print ("Hello world\n")

Когда вы запускаете это, ваше приглашение Unix появляется в строке, следующей за Hello world . Такие символы, как ‘ \ n ‘, являются метасимволами, которые указывают на выходные данные, отличные от стандартных символов, таких как буквы, цифры и символы.

Теперь вы могли бы достичь того же результата, написав.

1
println ("Hello world")

Функции print и println одинаковы, за исключением того, что последний всегда добавляет новую строку в конце своего вывода — то, что часто желательно и, таким образом, упрощает жизнь программиста. Тем не менее, нам все еще часто приходится использовать символ новой строки и другие символы при выводе строк. Например, поместите следующее в HelloWorld.scala и запустите его снова.

1
println("Hello world\nHere is a list:\n\t1\n\t2\n\t3")

Из вывода должно быть совершенно ясно, что означает « \ t ». Обратите внимание, что нет необходимости ставить ‘ \ n ‘ после последних 3, потому что println использовался вместо print .

Это тривиальная программа, но в целом они, как правило, становятся достаточно сложными. Здесь комментарии кода пригодятся. Вы можете указать, что строка должна игнорироваться Scala в качестве комментария, используя две косые черты. Комментарии могут использоваться, чтобы указать, кто является автором программы, какая у нее лицензия, документация, помогающая другим (и вашей будущей личности) понять, что делают различные части кода, и комментирование строк кода, которые вы делаете не хотите стирать, но вы хотите временно неактивны.

Вот немного более длинная программа с комментариями и определениями функций и использованием этих функций наряду с печатью.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
// Author: Jason Baldridge (jasonbaldridge@gmail.com)
  
// This is a trivial program for students learning to program with Scala.
  
// This is a comment. The next line defines a function that squares
// its argument.
def sq (x: Int) = x * x
  
// The next line prints the result of calling sq with the argument 3.
println("3 squared = " + sq(3))
  
// The next line is commented out, so even though it is a valid Scala
// expression, it won't be evaluated by Scala.
// println("4 squared = " + sq(4))
  
// Now, we define a function that uses the previously defined sq
// (rather than using x*x and y*y as before).
def squareThenAdd (x: Int, y: Int) = sq(x) + sq(y)
  
// Now we use it.
println("Squaring 3 and 4 and adding the results = "
        + squareThenAdd(3,4))

Сохраните это как ScalaFirstStepsPart1.scala и запустите его с исполняемым файлом Scala. Вы должны увидеть следующие результаты.

1
2
3
$ scala ScalaFirstStepsPart1.scala
3 squared = 9
Squaring 3 and 4 and adding the results = 25

Выглядит хорошо, правда? Но что происходит с этими печатными заявлениями? Ранее мы видели, что 2 + 3 оценивается как 5, но что «2? +» 3? оценивается как «23?», и здесь мы использовали + для String и Int. Разве это не должно привести к ошибке? Scala автоматически конвертирует Int в строку для нас, что значительно упрощает вывод результатов. Это означает, что мы можем сделать что-то вроде следующего (вернуться к использованию REPL).

1
2
scala> println("August " + 22 + ", " + 2011)
August 22, 2011

Это кажется немного бессмысленным, потому что мы могли бы просто написать «22 августа 2011 года?», Но вот пример, где это немного более полезно: мы можем назвать завтрашний день, используя Int для сегодняшнего дня и добавляя один к нему.

1
2
3
4
5
scala> val dayOfTheMonthToday = 22
dayOfTheMonthToday: Int = 22
  
scala> println("Today is August " + dayOfTheMonthToday + " and tomorrow is August " + (dayOfTheMonthToday+1))
Today is August 22 and tomorrow is August 23

Обратите внимание, что часть (dayOfTheMonthToday + 1) на самом деле представляет собой дополнение Int, и результат этого преобразования преобразуется в строку, которая объединяется с остальной частью строки. Этот пример все еще довольно надуманный (и, очевидно, не относится к концу месяца и т. Д.), Но это автоконверсия часто используется, когда вы начинаете работать с более сложными программами. И, может быть, даже более очевидно, мы могли бы разумно захотеть добавить Int и Double.

1
2
scala> 2 + 3.0
res27: Double = 5.0

Здесь результатом является Double, так как это более общий тип, чем Int. Этот вид автоконверсии часто случается, и часто вы даже не понимаете, что это происходит.

Еще одна вещь, которую стоит отметить, это то, что последний оператор print прошел несколько строк. Мы узнаем больше о правилах для операторов, которые выполняются по нескольким строкам, но этот пример показывает, пожалуй, самый простой для запоминания: когда вы открываете скобку с помощью «(», вы можете продолжать идти по нескольким строкам до тех пор, пока он не станет его партнером. «)» Встречается. Так, например, мы могли бы сделать следующее очень распространенное утверждение.

1
2
3
4
5
6
7
println(
  "Squaring 3 and 4 and adding the results = "
  
  +
  
  squareThenAdd(3,4)
)

Помимо использования для упаковки в коде, который является аргументом функции, как указано выше, круглые скобки весьма полезны для указания порядка приоритета объединения нескольких элементов, например, для указания того, что сложение должно быть выполнено перед умножением. или что добавление Int должно быть сделано до конкатенации строк, как показано ранее в руководстве. В основном, круглые скобки часто являются необязательными, но если вы не можете вспомнить, каковы правила по умолчанию для выражений, сгруппированных вместе, то вы можете явно сгруппировать их с помощью круглых скобок.

Ссылка: Первые шаги в Scala для начинающих программистов, часть 1 от нашего партнера по JCG Джейсона Болдриджа в блоге Bcomposes .

Статьи по Теме :