Подготовка к тестовому в DINS Scala School

Подготовка к тестовому в DINS Scala Schoolscala

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

Самое главное

Типы

Int

Int — целое число, хранится в ячейке памяти длиной 32 бита.

String

String — строка любой длины. Записывается в двойных кавычках.

Boolean

Boolean — логическое значение. Записывается как true или false, обязательно маленькими буквами.

Любые значения одного типа можно проверить на равенство (==) или неравенство (!=).

Попробовать вживую

Если хочется запустить код из примеров или написать свой, воспользуйся Scastie, он работает в браузере, скачивать ничего не нужно. Убедись, что настройка Worksheet в панели над текстовой областью включена, и напиши что-нибудь. Чтобы запустить, нажми кнопку Save.

После запуска напротив каждой строчки появится результат её выполнения. Если вызван println(), вывод из него будет в консоли внизу. Например, println(10) выведет в консоль 10.

Переменные

Переменные можно объявлять с помощью ключевого слова var. Значения таких переменных можно изменять в любой момент.

Типы переменных могут быть выведены автоматически, но можно и явно указать тип, как показано ниже:

Обрати внимание, что объявление типа Int происходит после идентификатора x, следующим за двоеточием.

Значения

Результаты выражений можно присваивать именам с помощью ключевого слова val.

Такие переменные называются значениями. Вызов значения не приводит к его повторному вычислению.

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

Как и в случае с переменными, можно явно указать тип:

Методы

Методы задаются ключевым словом def. За def следует имя, список параметров, возвращаемый тип и тело.

Обрати внимание, как объявлен возвращаемый тип сразу после списка параметров и двоеточия : Int.

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

Или вообще ни одного списка параметров.

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

Последнее выражение в теле становится возвращаемым значением метода.

Когда не имеет смысла что-либо возвращать, используется тип Unit (аналогично void в Java и C). Поскольку каждое выражение Scala должно иметь какое-то значение, то при отсутствии возвращающегося значения вместо него используется экземпляр типа Unit. Явным образом его можно задать как (), он не несет какой-либо информации.

List

List — это односвязный неизменяемый список. Каждый элемент списка содержит значение и ссылку на следующий элемент списка. Последний элемент содержит ссылку на пустой список, который не содержит никаких значений. Для List пустой список называется Nil. Значения и их порядок нельзя изменить, как мы бы это сделали с массивом, но можно создать новый список, с другими элементами.

Первый элемент списка называют головой списка (head), а всю остальную его часть — хвостом (tail).

List можно создать так:

Также можно указать тип списка: тип элементов внутри указывается в квадратных скобках после слова List. Это может быть полезно при объявлении входных и выходных параметров методов и при создании пустого List: компилятору негде взять информацию о типе элементов внутри.

Чтобы создать список определенного размера с одинаковыми элементами, можно использовать List.fill:

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

Обрати внимание, что поскольку список неизменяемый, все обновления записываются в новые значения val.

map

У List есть метод map. Он принимает на вход функцию и возвращает последовательность, в которой каждый элемент заменен на результат применения этой функции к элементу изначальной коллекции.

Функция, которую мы передали в качестве входного параметра, состоит из двух частей: слева от стрелочки (=>) находится название переменной, в которой во время выполнения map окажется один из элементов последовательности, а справа — блок кода, который использует эту переменную, чтобы вычислить новое значение элемента списка.

Блок кода справа от стрелочки может состоять из нескольких строк, но его придется взять в фигурные скобки:

Название переменной слева от стрелочки можно придумать любое.

flatMap

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

Паттерн-матчинг

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

Последняя ветвь выражения (case _ =>) не накладывает никаких ограничений, то есть будет выполнена, если никакие из ветвей, объявленных выше, не сработают.

Вместо _ можно объявить значение и использовать его в части после =>, например:

Таким образом, если после слова case написано конкретное значение (например, false, 0 или "десять"), то будет проверено, соответствует ли переменная этому значению. Если после слова case написать название новой переменной, то эта переменная будет объявлена со значением изначальной переменной (в примере выше значение other совпадает со значением int). Вместо названия новой переменной можно использовать символ _, тогда новая переменная не создастся.

К выражению case можно добавить условие:

Паттерн-матчинг выполняется построчно сверху вниз: для каждого case проверяется, выполняются ли условия, описанные в нём. Если да, выполняется часть выражения после =>, а следующие case игнорируются. Если нет, проверяется следующий case. Если все case проверены, а подходящего так и не нашлось, паттерн-матчинг завершается с ошибкой.

Паттерн-матчинг может использоваться внутри map и flatMap, тогда название переменной и слово match можно пропустить:

Паттерн-матчинг и List

List позволяет разделить себя на голову и хвост прямо в выражении case, если используется оператор ::. Слева от этого оператора будет записана голова, а справа — хвост.

:: матчит любой непустой список. Список из одного элемента будет разделен на значение этого элемента и пустой список. Пустой список так разделить не получится, поэтому для него нужна отдельная ветвь: case Nil => ...

Хвост можно таким же образом распаковать с помощью оператора :: в том же выражении case.

Например:

Обрати внимание, если мы попробуем разделить список на больше частей, чем он содержит, будет считаться, что это выражение case не сматчилось, и будет проверен следующий case:

Tuple

Tuple позволяет объединить несколько переменных в одной. Эти переменные могут быть одного или разных типов. К переменной внутри tuple можно обратиться по номеру:

На практике к элементам tuple чаще не обращаются по номеру, а распаковывают во время паттерн-матчинга:

У последовательностей есть метод zipWithIndex, который из каждого элемента последовательности создает tuple, состоящий из номера этого элемента и самого элемента последовательности. Элементы последовательностей, в отличие от элементов tuple, нумеруются с 0.

Хвостовая рекурсия

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

Можно добавить к функции аннотацию @tailrec: сборка завершится ошибкой, если функция с такой аннотацией использует нехвостовую рекурсию или не использует рекурсию совсем. Обрати внимание, что для использования этой аннотации в начале файла должна быть строка import scala.annotation.tailrec, так как этой аннотации нет в стандартной области видимости.

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

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

Входной тест

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

Потренироваться можно на задачах из раздела «Working with lists» из Ninety-Nine Scala Problems.

Пример задачи

Напишите метод, который заменяет все вхождения элементов, равных replacing, на элемент replacement. Скопируйте объявление метода replace в Scastie и замените ??? на решение задачи

Варианты решения:

Вариант 1, с использованием map:

Вариант 2, с использованием рекурсии:

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *