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

Синтаксис

Синтаксис сопоставления с примером состоит из значения, ключевого слова match (сопоставить) и по крайней мере, одного пункта с примером case, с которым мы хотим сопоставить наше значение.

import scala.util.Random

val x: Int = Random.nextInt(10)

x match {

case 0 => "zero"

case 1 => "one"

case 2 => "two"

case _ => "many"

}

40. Что такое замыкание?

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

Пример:

Если мы определим функцию, как показано ниже:

def example(a:double) = a*p / 100

Теперь, запустив приведенный выше код, мы получим ошибку, начиная с not found p. Итак, теперь мы даем значение p вне функции.

// определил значение p как 10

val p = 10

// определить это закрытие.

def example(a:double) = a*p / 100

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

Calling the function: example(10000)

Input: p = 10

Output: double = 1000.0

Что теперь, если значение свободной переменной изменяется, как изменяется значение функции закрытия?

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

Input: p = 10

Output: double = 1000.0

Input: p = 20

Output: double = 2000.0

41. Что такое частично применяемые функции?

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

В Scala, когда в функцию передаются только некоторые параметры для создания другой функции, она называется

частичным применением этой функции.

Итак, рассмотрим функцию:

def minus(a: Int, b: Int) = a-b

Теперь давайте частично применим эту функцию, передав некоторые параметры и сделав другую функцию.

val minus50 = (a: Int) => minus(a, 50);

В этом случае

minus50является

частичным применением минуса.

Частичные функции

Частичная функция — это функция, которая действует только для подмножества. Например, рассмотрим математическую функцию, где x задается из всех чисел от 1 до 100:

f (x) = x + 5;

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

Так что, если мы хотим определить только функцию

f(x) = x + 5

для чисел 1,2,3,4,5,6,7, но не для 8,9,10, … — мы определяем частичную функцию.

f(x')=x+5

где x ‘= {1,2,3,4,5,6,7}

42. Тестирование в Scala

Для тестирования имеются такие библиотеки, как: ScalaCheck, ScalaTest, ScalaMock.

ScalaTest - фреймворк для тестирования приложений, поддерживающий разные стили написания тестов и легко интегрирующееся с другими инструментами для JVM. Для использования каждого из стилей тестирования необходимо создать класс, который будет реализовывать трейт, в котором этот тип тестирования будет определен. Например, FlatSpec для Unit-тестов и интеграционного тестирования. А FeatureSpec - для приемочного тестирования.

Стиль тестирования влияет только на декларации тестов, а возможности фреймворка будут работать одинаково.

В каждом стиле по умолчанию доступно 3 ассерта:

Assert - для обычных проверок

AssertResult - для проверки совпадения полученного и ожидаемого результата

Intercept - для проверки того, что метод бросает ожидаемое исключение

43. Что такое type класс?

В Scala 3 классы типов - это просто черты с одним или несколькими параметрами типа, например:

trait Show[A]:

def show(a: A): String

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

Он помогает бороться с лишними зависимостями и в то же время делать код чище.

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

44. Что такое функциональная композиция?

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

Существует несколько разных способов, с помощью которых можно это реализовать:

  1. compose

(function1 compose function2)(parameter)

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

  1. andThen

(function1 andThen function2)(parameter)

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

или так же, как показано ниже:

function2(function1(parameter))

45. Метод apply

46. Исключения в Scala

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

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

throw new IllegalArgumentException("x should not be negative")

Вычисления прерываются, и среда времени выполнения пытается отыскать обработчик исключения IllegalArgumentException. Затем управление передается самому внутреннему такому обработчику. Если обработчик не будет найден, программа завершится. В отличие от Java, в Scala отсутствуют «контролируемые» (checked) исключения – вам никогда не придется объявлять, что некоторый метод или функция может возбуждать исключение

(пример из джавы: public void writeList() throws IOException {//теперь нам не нужно самим обрабатывать исключение})

Рассмотрим пример, где имеется тип возвращаемого значения:

val half =

if (n % 2 == 0)

n / 2

else

throw new RuntimeException("n must be even")

Здесь получается, что, если n является четным числом, переменная half будет инициализирована половинным значением n. Если n не является четным числом, исключение будет выдано еще до того, как переменная half вообще будет инициализирована каким-либо значением.

Перехватить исключение можно при помощи блока try-catch. Синтаксис перехвата исключений основан на синтаксисе блока case-match:

val url = new URL("http://horstmann.com/fred­tiny.gif")

try {

process(url)

} catch {

case _: MalformedURLException => println(s"Bad URL: $url")

case ex: IOException => ex.printStackTrace()}

Вместо имени переменной можно использовать _, если потом эта переменнаяне нужна.

Инструкция try/finally позволяет освободить занятые ресурсы независимо от наличия или отсутствия исключения. Например:

val in = new URL("http://horstmann.com/fred.gif").openStream()

try {

process(in)

} finally {

in.close()

}

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

https://www.iprbookshop.ru/87976

http://uchcom7.botik.ru/L/prog/java/Scala.prof.prog.pdf

47. Ленивые значения

Когда значение val объявляется как lazy (ленивое), его инициализация откладывается до первого обращения к нему. Ленивые (lazy) значения удобно использовать, чтобы отложить операции инициализации. Если перед определением val-переменной поставить модификатор lazy, выражение инициализации в правой стороне будет вычисляться только при первом использовании val-переменной.

Определим, например, объект Demo с val-переменной:

scala> object Demo {

val x = {

println("initializing x");

"done"

}

}

defined object Demo

Теперь сначала сошлемся на Demo, а затем на Demo.x:

scala> Demo

initializing x

res3: Demo.type = Demo$@2129a843

scala> Demo.x

res4: String = done

Как видите, на момент использования объекта Demo его поле x становится проинициализированным. Инициализация x составляет часть инициализации Demo. Но ситуация изменится, если определить поле x в качестве ленивого:

scala> object Demo {

lazy val x = {

println("initializing x");

"done"

}

}

defined object Demo

scala> Demo

res5: Demo.type = Demo$@5b1769c

scala> Demo.x

initializing x

res6: String = done

Теперь инициализация Demo не включает инициализацию x. Такая инициализация будет отложена до первого использования x. Это похоже на ситуацию определения x в качестве метода без параметров с использованием ключевого слова def. Но, в отличие от def, ленивая val-переменная никогда не вычисляется более одного раза. Фактически после первого вычисления ленивой val-переменной результат вычисления сохраняется, чтобы его можно было применить повторно при последующем использовании той же самой val-переменной.

https://www.iprbookshop.ru/87976

http://uchcom7.botik.ru/L/prog/java/Scala.prof.prog.pdf

48. Абстрактные классы Scala

Абстракция — это процесс скрытия внутренних деталей и отображения только функциональности. В Scala абстракция достигается с помощью абстрактного класса. Работа абстрактного класса Scala аналогична абстрактному классу Java . В Scala абстрактный класс создается с использованием ключевого слова abstract . Он содержит как абстрактные, так и не абстрактные методы и не может поддерживать множественное наследование. Класс может расширять только один абстрактный класс

Абстрактные методы абстрактного класса — это те методы, которые не содержат никакой реализации. Или, другими словами, метод, который не содержит тела, называется абстрактным методом.

Ниже приведены некоторые важные замечания об абстрактных классах в Scala.

○ Как и в Java, в Scala нам не разрешено создавать экземпляр абстрактного класса. Если мы попытаемся создать объекты абстрактного класса, то компилятор выдаст ошибку.

○ В Scala абстрактный класс также может содержать поля. К этим полям обращаются методы абстрактного класса и методы класса, которые наследуют абстрактный класс.

○ Как и в Java, в Scala абстрактный класс также может содержать конструктор, и конструктор абстрактного класса вызывается при создании экземпляра унаследованного класса.

○ Абстрактный класс также может содержать только не абстрактный метод. Это позволяет нам создавать классы, которые не могут быть созданы, но могут быть унаследованы.

○ В Scala абстрактный класс может содержать конечные методы (методы, которые нельзя переопределить).

Когда использовать абстрактный класс в Scala:

● Когда мы хотим создать базовый класс, который нуждается в аргументах конструктора.

● Когда наш код будет вызываться из кода Java.

49. Синглтон

Синглтон как паттерн программирования подразумевает, что у класса одновременно существовать может только один экземпляр. В Scala все объекты являются singleton (т.е. существуют в единственном экземпляре). Они создаются лениво, когда на него ссылаются (так же как lazy val).

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

Важные моменты об одиночном объекте

  • Метод в одноэлементном объекте доступен глобально.

  • Вам не разрешено создавать экземпляр одноэлементного объекта.

  • В Scala одноэлементный объект может расширять класс и характеристики.

  • В Scala основной метод всегда присутствует в одноэлементном объекте.

  • Доступ к методу в одноэлементном объекте осуществляется по имени объекта (как при вызове статического метода в Java), поэтому нет необходимости создавать объект для доступа к этому методу.

50. Объекты-компаньоны

Объект с тем же именем, что и класс называется объект компаньон (companion object). И наоборот, класс является классом-компаньоном объекта. Класс или объект компаньон может получить доступ к приватным членам своего спутника. Используйте объект компаньон для методов и значений, которые не специфичны для экземпляров класса компаньона.

51. Почему в Scala не поддерживается множественное наследование?

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

trait Planar {

def height: Int

def width: Int

def surface = height * width

}

...

class Square extends Shape with Planar with Movable

Заметки о функциональном программировании. Наследование и полиморфизм. (bavadim.me) term5-scala (cdkrot.me)

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