Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Вопросы ИТ.docx
Скачиваний:
4
Добавлен:
01.04.2022
Размер:
759.42 Кб
Скачать

PS- МАЛО ИНФЫ О АНОНИМНЫХ ФУНКЦИЯХ и по тайп классам

http://bavadim.me/programming/2015/01/19/Notes-about-FP.html -ИНФА ХОРОШАЯ О КАРРИРОВАНИИ, АНОНИМНОЙ ФУНКЦИИ И ФУНКЦИЙ ВЫСШЕГО ПОРЯДКА

ВОПРОСЫ К ИЗОТОВОЙ:

  1. ЧТО МОГУТ СПРОСИТЬ ПРО АНОНИМНЫЕ ФУНКЦИИ

  2. ЧЕМ SOME И NONE ЛУЧШЕ NULL, что еще могут спросить

  3. Как получить доступ к Scala REPL из командной строки?

  4. Насколько глубоко нужно объяснять вопросы, где спрашивают : “что такое ….?”?

Вопросы к экзамену

1. В чем разница между следующими понятиями: ‘Nil,’ ‘Null,’ ‘None,’ ‘Nothing’?

Nil - это конец списка.

Null – отсутствие значения. Null – это тип, который представляет отсутствие информации для сложных типов данных, наследуемых от AnyRef

None - это значение Option, если в ней ничего нет

Nothing – это низший тип всей системы Scala, включающий все типы AnyVal и AnyRef. Nothing обычно используется как return из метода, который сработал с ошибкой или выдал исключение.

2. Что такое ‘Option’ и как его используют?

Option - это контейнер, который предоставляет возможность различия внутри системы типов, которые могут иметь нулевые значения и тех, которые не могут. Значение можно получить с помощью get(), но предпочтительнее использовать функции высшего порядка вроде map, flatMapand foreach. Использовать map нужно, когда необходимо вернуть значение другой Option, flatmap для того, чтобы сгруппировать несколько значений Option вместе, foreach для операций, которые влияют на значение внутри Option без возвращения нового значения и для того, чтобы вывести их сочетание кратко и ясно.

3. Что такое ‘Unit’ и ‘()’?

Unit - это тип, который является эквивалентом void в Java, но при этом обеспечивает язык абстракцией над платформой Java. Пустые круглые скобки – представляют значение Unit

4. В чем преимущество Scala?

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

Доп: Это мультипарадигменный язык. То есть, для работы со Scala можно использовать как объектно-ориентированный подход (ООП), так и функциональный. На практике, обе эти концепции можно применять даже совместно, используя гибридную модель разработки, позволяющую брать все самое лучшее от обоих подходов. В итоге, мы получаем синергию ООП и ФП.

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

5. Объясните, что такое Scala?

Scala расшифровывается как Scalable Language. Это многопарадигмальный язык программирования. Он поддерживает как объектно-ориентированный, так и функциональный язык программирования. Это работает для JVM (виртуальная машина Java)

6. Что такое map?

Map это Iterable состоящее из пар ключ значение (также называемых связкой или ассоциативным массивом). Scala Объект Predef предоставляет неявное преобразование, позволяющее записать пару (ключ, значение) используя альтернативный синтаксис вида ключ -> значение . Например, Map("x" -> 24, "y" -> 25, "z" -> 26) означает тоже самое что и Map(("x", 24), ("y", 25), ("z", 26)), но читается лучше.

Основные операции на мапах аналогичны тем же операциям на множества. Рассмотрим в следующей таблице обобщенный и сгруппированный по категориям список методов на мапах:

  • Запросы операции apply, get, getOrElse, contains, и isDefinedAt. Они превращают мапы в частично определенные функции от ключей к значениям. Основной “запросный метод” на мапах это : def get(key): Option[Value]. Операция “m get key” проверяет содержит ли мапа связанное значение для ключа key. Если да, то возвращает это связанное значение обернутое в Some. Если же нет, то get возвращает None. На мапах еще определен метод apply, которое напрямую возвращает связанное с заданным ключем значение, без оборачивания его в Option. В этом случае, когда ключ не определен, будет брошено исключение.

  • Добавление и обновления +, ++, updated, которые позволяют добавлять новые пары к мапам или изменять существующие.

  • Удаления -, --, которые позволяют удалять пары из мап.

  • Создание подколлеций keys, keySet, keysIterator, values, valuesIterator, которые возвращают ключи и значения мап отдельно в различных формах.

  • Трансформации filterKeys и mapValues, которые создают новую мапу через фильтрацию и преобразования элементов существующей мапы.

СОЗДАНИЕ

Создание пустой карты, которую можно использовать в качестве значения по умолчанию. Мы можем использовать пустой метод для сопутствующего объекта карты:

val emptyMap: Map[Int, String] = Map.empty[Int, String]

Другой способ создания пустой карты - использовать метод apply:

val emptyMap: Map[Int, String] = Map[Int, String].apply()

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

val emptyMap: Map[Int, String] = Map[Int, String]()

Полное руководство по картам: https://www.baeldung.com/scala/maps-guide

7. Чем Scala лучше других языков программирования?

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

  2. Scala может оптимизировать Java-код.

8. Переменные в Scala

Как и в любом другом языке, переменная – это именованный зарезервированный участок памяти. Другими словами, когда мы объявляем переменную, мы резервируем какое-то место в памяти.

В Scala предусмотрено несколько способов объявления переменных. Ниже приведён пример объявления переменной с помощью ключевого слова var:

var someVariable : String = "Some String";

То, что мы объявили данную переменную, как var, говорит о том, что она может быть изменена (mutable).

Тип данных переменной

В Scala тип данных переменной указывается после имени переменной и перед знаком равенства.

Ниже приведены несколько примеров:

val simpleString : String;

var simpleInteger: Int;

Кроме того, компилятор Scala может автоматически присвоить тип данных переменной, основываясь на значении, которое мы её присваиваем. Данный механизм называется “Предположением типа переменной”.

Пример:

val simpleString = "This is simple String";

В языке программирования Scala поддерживается множественное присваивание. Данная операция имеет следующий общий вид:

val (simpleInteger: Int, simpleString: String) = Pair(100500, "String");

либо:

val (simpleInteger, simpleString) = Pair(100500, "String");

Область видимости переменных

В Scala переменная может иметь одну из трёх областей видимости, в зависимости от того, где именно она была объявлена:

  • Поле класса Поле является характеристикой класса. Они доступны внутри каждого метода данного класса и за его пределами, в зависимости от модификатора доступа, который к ним применяется.

  • Локальная переменная Локальная переменная – это переменная, которая объявляется внутри метода. Они доступны только внутри данного метода, но объекты могут передать их значения, если данный метод возвращает это значение.

  • Параметр метода Данный вид переменных передаётся в метод, как параметр или один из параметров. Они доступны внутри метода, либо за его пределами, если данный метод возвращает значение.

9. Укажите разницу между объектом и классом?

  1. Классы, в отличие от объектов, могут принимать различные параметры. Пример: Объект:

object Logger {

def info(message: String): Unit = println(s"INFO: $message")

}

Класс:

class Point(var x: Int, var y: Int) {

def move(dx: Int, dy: Int): Unit = {}

}

  1. Чаще всего объект нужен для хранения методов и значений/переменных, которые будут доступны без необходимости сначала создавать экземпляр какого-либо класса. Это использование тесно связано со статическими членами в Java:

object A {

def twice(i: Int): Int = 2*i

}

Затем можно вызвать вышеуказанный метод, используя A.twice(2).

Если бы twice был членом некоторого класса A, то сначала нужно было бы создать его экземпляр:

class A() {

def twice(i: Int): Int = 2 * i

}

val a = new A()

a.twice(2)

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

Классы и объекты (groz.github.io)

Классы | Scala Documentation (scala-lang.org)

Объекты Одиночки | Scala Documentation (scala-lang.org)

10. Что такое хвостовая рекурсия?

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

Мы можем понять хвостовую рекурсию вот так

  • Все рекурсивные формы вызовов появляются в конце функции.

  • Рекурсивный вызов - это последний оператор, выполняемый во всем теле функции, и его возвращаемое значение не является частью выражения.

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

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

11. Что такое трейт?

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

Минимальное объявление трейта - это просто ключевое слово trait и его имя:

trait HairColor

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

Есть и отличие от интерфейса, которое заключается в том, что в трейте вы сможете описать поля класса, а также частично реализовывать методы. Причем наследование и трейтов, и абстрактных классов реализуется посредством extend (это первый родитель) и with (это последующие родители).

Что же, осталось посмотреть пример использования:

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

12. Когда нужно использовать трейты?

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

Трейты | Scala Documentation (scala-lang.org)

13. Что такое кейс классы?

Классы образцы (Case classes) похожи на обычные классы с несколькими ключевыми отличиями. Классы образцы хороши для моделирования неизменяемых данных

Минимальный вариант объявления класса образца: указание ключевого слова case class, название и список параметров (которые могут быть пустыми). Пример:

case class Book(isbn: String)

val frankenstein = Book("978-0486282114")

Обратите внимание, что ключевое слово new не было использовано для создания экземпляра класса Book. Это связано с тем, что классы образцы по умолчанию имеют объект компаньон с методом apply, который берет на себя заботу о создании экземпляра класса.

При создании класса образца с параметрами, эти параметры являются публичными и неизменяемыми.

Сравнение

Классы образцы сравниваются по структуре, а не по ссылкам:

case class Message(sender: String, recipient: String, body: String)

val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")

val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?")

val messagesAreTheSame = message2 == message3 // true

Даже если message2 и message3 ссылаются на разные объекты, значения каждого из них равны.

Копирование

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

В Scala есть очень мощная система сопоставления объекта с образцом. Однако экземпляр далеко не каждого класса можно сопоставлять с образцом без лишних телодвижений. Для упрощения этого процесса были введены два специальных вида сущностей: кейс-классы (case classes) и кейс-обжэкты (case objects). Кейс-классы подобны именованным кортежам, используемым для хранения данных. Часто они могут не иметь своих методов, выступая пассивными контейнерами, ведь Scala предоставляет великое множество других методов взаимодействия.

14. Что такое кортежи?

В Scala, кортеж (Тuple) это класс контейнер содержащий упорядоченный набор элементов различного типа.

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

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

val ingredient = ("Sugar" , 25):Tuple2[String, Int]

Такая запись создает кортеж размерности 2, содержащий пару элементов String и Int.

Кортежи в Скале - представлены серией классов: Tuple2, Tuple3 и т.д., до Tuple22. Таким образом, создавая кортеж с n элементами (n лежащими между 2 и 22), Скала просто создает один из соответствующих классов, который параметризован типом входящих в состав элементов.

В нашем примере, составляющие тип Tuple2[String, Int].

Доступ к элементам

Доступ к элементам кортежа осуществляется при помощи синтаксиса подчеркивания. ‘tuple._n’ дает n-ый элемент (столько, сколько существует элементов).

println(ingredient._1) // Sugar

println(ingredient._2) // 25

Распаковка данных кортежа

Scala кортежи также поддерживают распаковку.

val (name, quantity) = ingredient

println(name) // Sugar

println(quantity) // 25

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

15. Что такое неявные параметры в Scala?

Неявный параметр — это параметр функции, которому предшествует ключевое слово implicit. Оно значит, что, если не передано никакого значения при вызове функции, то компилятор собственноручно будет искать неявный параметр и передаст его за нас. Места Scala будут искать эти параметры в двух категориях:

  1. Сначала Scala ищет неявные определения и неявные параметры, к которым можно получить доступ напрямую (без префикса) в точке, где вызывается метод с неявным блоком параметров.

  2. Затем он ищет элементы, помеченные как неявные во всех объектах-компаньонах, связанных с неявным типом кандидата.

Пример:

def double(implicit value: Int) = value * 2

implicit val multiplier = 2

println(double) // 4

Tour-of-Scala/implicit.md at master · steklopod/Tour-of-Scala (github.com)

Неявные (implicit) параметры и преобразования в Scala / Хабр (habr.com)

Неявные Параметры | Scala Documentation (scala-lang.org)

16. Что такое карирование функций?

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

Иногда требуется передача каких-то аргументов в вашу функцию прямо сейчас, а других через некоторое время.

17. Что такое анонимная функция?

Анонимная функция в программировании — особый вид функций, которые объявляются в месте использования и не получают уникального идентификатора для доступа к ним

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

Следующее выражение определяет принимает входной параметр типа Int анонимной функции:

var inc = (x:Int) => x+1

Объем анонимной функции очень мал, обычно анонимная функция используется как параметр другой функции.

18. Что такое функции высшего порядка?

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

Такое возможно поскольку функции являются объектами первого класса в Scala.

Одним из наиболее распространенных примеров функции высшего порядка является функция map, которая доступна в коллекциях Scala.

val salaries = Seq(20000, 70000, 40000)

val doubleSalary = (x: Int) => x * 2

val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)

doubleSalary - это функция, которая принимает один Int x и возвращает x * 2. В общем случае, кортеж (список имен в скобках) слева от стрелки => - это список параметров, а значение выражения следует справа. Это же значение возвращается в качестве результата. В строке 3 к каждому элементу списка зарплат (salaries) применяется функция doubleSalary.

Чтобы сократить код, мы можем сделать функцию анонимной и передать ее напрямую в качестве аргумента в map:

val salaries = Seq(20000, 70000, 40000)

val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)

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

19. В чем разница между var и val?

в Scala есть два типа переменных:

  • val создает неизменяемую переменную (как finalв Java)

  • var создает изменяемую переменную

В Scala общее правило состоит в том, что вы всегда должны использовать val поле, если нет веской причины не делать этого.

Это простое правило

(а) делает ваш код более похожим на алгебру

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

20. Что такое some и none?

Опция – это коллекция элементов заданного типа.

Для создания Option используется запись следующего вида:

def findDeveloper(key: String): Option[Developer];

Данный метод вернёт Some[Developer] если запись будет найдена в указанной коллекции, либо None – если нет.

Some и None являются дочерними элементами Option

21. Как можно форматировать строки?

Scala предлагает механизм для создания строк из ваших данных - интерполяцию строк. Интерполяция строк позволяет пользователям вставлять ссылки на переменные непосредственно в обработанные строковые литералы. Вот пример:

val name = "James"

println(s"Hello, $name") // Hello, James

В приведенном выше примере литерал s"Hello, $name" является обработанным строковым литералом. Это означает, что компилятор выполняет некоторую дополнительную работу с этим литералом. Обработанный строковый литерал обозначается набором символов, предшествующих двойным кавычкам.

Scala предоставляет три метода интерполяции строк из коробки: s, f и raw.

Строковый интерполятор «s»

Добавление s к любому строковому литералу позволяет использовать переменные непосредственно в строке. Вы уже видели пример здесь:

val name = "James"

println(s"Hello, $name") // Hello, James

Здесь $name вложена внутри обработанной строки “s”. Интерполятор “s” знает, как вставить значение name переменной в этом месте в строку, в результате чего получается строка Hello, James. С помощью интерполятора “s” в строке можно использовать любое имя, находящееся в области видимости.

Строковые интерполяторы также могут принимать произвольные выражения. Например:

println(s"1 + 1 = ${1 + 1}")

Будет напечатана строка 1 + 1 = 2. Любое произвольное выражение может быть встроено в ${}.

Для некоторых специальных символов необходимо экранировать их, когда они встроены в строку. Чтобы представить реальный знак доллара, вы можете удвоить его $$, как здесь:

println(s"New offers starting at $$14.99")

Будет вывод строки New offers starting at $14.99.

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

val person = " " "{"name":"James"}" " "

Что приведет к появлению строки {"name":"James"} при печати.

Интерполятор “f”

Добавление “f” к любому строковому литералу позволяет создавать простые форматированные строки. При использовании интерполятора “f” все ссылки на переменные должны сопровождаться строкой формата в стиле printf, например %d. Давайте рассмотрим пример:

val height = 1.9d

val name = "James"

println(f"$name%s is $height%2.2f meters tall") // James is 1.90 meters tall

Интерполятор “raw”

Данный интерполятор аналогичен интерполятору “s”, за исключением того, что он не выполняет экранирование литералов в строке. Вот пример обработанной строки:

scala> s"a\nb"

res0: String =

a

b

Здесь интерполятор строк “s” заменил символы возвращаемым символом \n. Интерполятор “raw” этого не сделает.

scala> raw"a\nb"

res1: String = a\nb

Интерполятор “raw” полезен, когда вы хотите избежать использования выражений, таких как превращение \n в возвращаемый символ.

https://docs.scala-lang.org/overviews/core/string-interpolation.html

https://www.geeksforgeeks.org/scala-format-and-formatted-method/

22. Типы литералов в Scala.

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