Учите Scala от 0 до 60: Основы

1656558369 uchite scala ot 0 do 60 osnovy

от Дурга Прасаны

6Y5SO3-umRdwDXeMVVnCr7oanap9xf6RF8FF
Фото Себастьяна Гроховича на Unsplash

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

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

Мы будем использовать Scala REPL для всех пояснений. Это очень удобный и информативный инструмент для изучения Scala. Он регистрирует милые маленькие сообщения о том, как наш код интерпретируется и выполняется.

Начнём сначала с основ.

1. Переменные

Мы можем определить неизменные переменные с помощью val:

scala> val name = "King"name: String = King

Сменные переменные могут быть определены и изменены с помощью var:

scala> var name = "King"name: String = King
scala> name = "Arthur"name: String = Arthur

Мы используем def чтобы назначить метку неизменному значению, оценка которого откладывается на более позднее время. Это означает, что значение этикетки лениво оценивается каждый раз при использовании.

scala> var name = "King"name: String = King
scala> def alias = namealias: String
scala> aliasres2: String = King

Вы заметили что-нибудь интересное?

При определении aliasзначение не было назначено alias: String поскольку он лениво ассоциируется, когда мы его вызываем. Что произойдет, если мы изменим значение name?

scala> aliasres5: String = King
scala> name = "Arthur, King Arthur"name: String = Arthur, King Arthur
scala> aliasres6: String = Arthur, King Arthur

2. Контрольный поток

Мы используем операторы потока управления для выражения логики нашего решения.

Вы можете написать if-else заявление, как показано ниже:

if(name.contains("Arthur")) {  print("Entombed sword")} else {  print("You're not entitled to this sword")}

Или вы можете использовать while:

var attempts = 0while (attempts < 3) {  drawSword()  attempts += 1}

3. Коллекции

Scala четко различает неизменные и изменяемые коллекции – прямо из самого пространства имен пакета ( scala.collection.immutable или scala.collection.mutable).

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

Но выполнение операций добавления, удаления или обновления неизменных коллекций возвращает новую коллекцию.

Неизменные коллекции всегда автоматически импортируются через файл scala._ (который также содержит псевдоним для scala.collection.immutable.List).

Однако, чтобы использовать изменяемые коллекции, вам нужно явно импортировать scala.collection.mutable.List.

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

Список

Мы можем создать список разными способами:

scala> val names = List("Arthur", "Uther", "Mordred", "Vortigern")
names: List[String] = List(Arthur, Uther, Mordred, Vortigern)

Другой удобный подход – определить список с помощью минусов :: оператор. Это соединяет главный элемент с остальным хвостом списка.

scala> val name = "Arthur" :: "Uther" :: "Mordred" :: "Vortigern" :: Nil
name: List[String] = List(Arthur, Uther, Mordred, Vortigern)

Что эквивалентно:

scala> val name = "Arthur" :: ("Uther" :: ("Mordred" :: ("Vortigern" :: Nil)))
name: List[String] = List(Arthur, Uther, Mordred, Vortigern)

Мы можем получить доступ к элементам списка непосредственно по их индексу. Помните, что Scala использует индексирование на основе нуля:

scala> name(2)
res7: String = Mordred

Некоторые распространенные вспомогательные методы включают в себя:

list.headвозвращающий первый элемент:

scala> name.head
res8: String = Arthur

list.tailвозвращающий хвост списка (включающий все, кроме головы):

scala> name.tail
res9: List[String] = List(Uther, Mordred, Vortigern)

Набор

Set позволяет создать неповторимую группу сущностей. List не устраняет дубликаты по умолчанию.

scala> val nameswithDuplicates = List("Arthur", "Uther", "Mordred", "Vortigern", "Arthur", "Uther")
nameswithDuplicates: List[String] = List(Arthur, Uther, Mordred, Vortigern, Arthur, Uther)

Здесь «Артур» повторяется дважды, как и «Утер».

Давайте создадим набор с теми же именами. Обратите внимание, как он исключает дубликаты.

scala> val uniqueNames = Set("Arthur", "Uther", "Mordred", "Vortigern", "Arthur", "Uther")
uniqueNames: scala.collection.immutable.Set[String] = Set(Arthur, Uther, Mordred, Vortigern)

Мы можем проверить наличие определенного элемента в Set с помощью contains():

scala> uniqueNames.contains("Vortigern")res0: Boolean = true

Мы можем добавлять элементы в набор с помощью метода + (принимающего varargs то есть аргументы переменной длины)

scala> uniqueNames + ("Igraine", "Elsa", "Guenevere")res0: scala.collection.immutable.Set[String] = Set(Arthur, Elsa, Vortigern, Guenevere, Mordred, Igraine, Uther)

Аналогично мы можем удалить элементы с помощью - метод

scala> uniqueNames - "Elsa"
res1: scala.collection.immutable.Set[String] = Set(Arthur, Uther, Mordred, Vortigern)

Карта

Map является итерационной коллекцией, содержащей отражение с key элементов к соответствующим value элементы, которые можно создать как:

scala> val kingSpouses = Map( | "King Uther" -> "Igraine", | "Vortigern" -> "Elsa", | "King Arthur" -> "Guenevere" | )
kingSpouses: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, Vortigern -> Elsa, King Arthur -> Guenevere)

К значениям для определенного ключа на карте можно получить доступ как:

scala> kingSpouses("Vortigern")res0: String = Elsa

Мы можем добавить запись на карту с помощью + метод:

scala> kingSpouses + ("Launcelot" -> "Elaine")res0: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, Vortigern -> Elsa, King Arthur -> Guenevere, Launcelot -> Elaine)

Чтобы изменить существующее сопоставление, мы просто снова добавляем обновленный ключ-значение:

scala> kingSpouses + ("Launcelot" -> "Guenevere")res1: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, Vortigern -> Elsa, King Arthur -> Guenevere, Launcelot -> Guenevere)

Обратите внимание, что поскольку коллекция неизменна, каждая операция редактирования возвращает новую коллекцию ( res0, res1) с примененными изменениями. Оригинальная коллекция kingSpouses остается неизменным.

4. Функциональные комбинаторы

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

Простыми словами Джона Хьюза:

Комбинатор – это функция, которая создает фрагменты программы из фрагментов программы.

Углубленный взгляд на то, как работают комбинаторы, выходит за рамки этой статьи. Но мы все равно попытаемся затронуть высокий уровень понимания этой концепции.

Давайте возьмем пример.

Предположим, мы хотим найти имена всех королев с помощью kingSpouses карту коллекции, которую мы создали.

Мы хотели бы сделать что-нибудь рядом с изучением каждой записи на карте. Если key имеет имя короля, то нас интересует имя его жены (т.е. королевы).

Мы будем использовать filter комбинатор на карте, который имеет такую ​​подпись:

collection.filter( /* a filter condition method which returns true on matching map entries */)

В общем, мы выполним следующие шаги, чтобы найти маток:

  • Найдите пары (ключ, значение) с именами королей как ключи.
  • Извлеките значения (имена queen) только для таких кортежей.

The filter является функцией, которая, когда дается (ключ, значение), возвращает true/false.

  1. Найдите на карте записи, относящиеся к королям.

Давайте определим нашу предикатную функцию фильтрации. Так как key_value является кортежем (ключ, значение), мы вытаскиваем ключ с помощью ._1 (и угадайте что ._2 возвращается?)

scala> def isKingly(key_value: (String, String)): Boolean = key_value._1.toLowerCase.contains("king")
isKingly: (key_value: (String, String))Boolean

Теперь мы воспользуемся функцией фильтра, определенной выше filter королевские записи.

scala> val kingsAndQueens = kingSpouses.filter(isKingly)
kingsAndQueens: scala.collection.immutable.Map[String,String] = Map(King Uther -> Igraine, King Arthur -> Guenevere)

2. Извлеките имена соответствующих королев из отфильтрованных кортежей.

scala> kingsAndQueens.values
res10: Iterable[String] = MapLike.DefaultValuesIterable(Igraine, Guenevere)

Давайте распечатаем названия королев с помощью foreach комбинатор:

scala> kingsAndQueens.values.foreach(println)IgraineGuenevere

Есть и другие полезные комбинаторы foreach, filter, zip, partition, find.

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

Давайте подытожим то, что мы научились:

  • Различные способы определения переменных
  • Различные операторы потока управления
  • Некоторые основы разных коллекций
  • Обзор использования функциональных комбинаторов в коллекциях

Надеюсь, эта статья была вам полезна. Это первая в серии статей по изучению Scala.

Во второй части мы узнаем об определении классов, признаков, инкапсуляции и других объектно-ориентированных концепций.

Пожалуйста, сообщите мне свои отзывы и предложения о том, как я могу улучшить содержание. А до этого ❤ кодировка.

Добавить комментарий

Ваш адрес email не будет опубликован.