Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лекции / 9

.pdf
Скачиваний:
102
Добавлен:
29.01.2021
Размер:
106.52 Кб
Скачать

§ Язык функционального программирования Haskell.

Язык Haskell – чисто функциональный язык программирования (к таким же языкам относятся ML, Lisp, Scala, Clojure, Agda, Idris). С функциями можно работать так же, как с любыми другими данными, то есть можно передавать в качестве аргументов другим функциям, возвращать в качестве результата или присваиваться переменным. Это упрощает запись и избавляет необходимости введения таких посредников, как итераторы и т. д.

Пример: переход от одного элемента списка к другому.

На языке Java:

Iterator it = sample_list.iterator(); while (it.hasNext()) {

Element node = it.next(); action(node); // выполняем действие

}

На языке Haskell (функции map передаем action в качестве параметра):

map sample_list action

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

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

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

Кроме того, к базовой концепции языка Haskell также относятся ленивые (отложенные) вычисления. Согласно этой концепции выражение вычисляется только тогда, когда результат его вычисления требуется для вывода или вычисления другого выражения. После вычисления результат или сбрасывается, или сохраняется, если он нужен для другого фрагмента кода. Наряду с минимизацией объема вычислений это приводит к необходимости сохранения в памяти еще не вычисленных (отложенных) выражений.

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

Примечание. Для некоторых языков со статической типизацией, таких как Java или C#, в период выполнения программы возникает необходимость дополнительной проверки типов. В отличие от этого после компиляции кода на языке Haskell проверка типов больше не проводится, что существенно повышает производительность.

Для кодирования на языке Haskell вначале необходимо установить платформу Haskell (http://www.haskell.org/platform/), которая содержит компилятор GHC, систему сборок и библиотек Cabal, а также полный набор библиотек. После установки платформы целесообразно проверить то, что она действительно работает.

Пример проверки: открыть консоль, набрать ghci –e 2+2 и нажать Enter. Должно появиться 4.

1

Затем можно установить удобную среду программирования на языке Haskell. Например, можно установить среду EclipseFP, которая устанавливается как плагин (надстройка) среды Eclipse. Дистрибутив самой среды находится на странице http://www.eclipse.org/downloads/), плагин устанавливается с помощью раздела Help меню Eclipse. Для проверки корректности установки можно опять же вызвать интерфейс компилятора GHC, подав команду ghci. Должно появиться приглашение основного модуля Prelude в виде Prelude>. Можно ввести выражение:

Prelude> 3 * 4

и проверить результат.

Здесь же можно и ввести и многострочные выражения, указав в начале блока символы :{ и :} в конце.

Пример:

Prelude> :{ Prelude| 2 + Prelude| 4 Prelude| :} 4

Система типизации в языке Haskell позволяет автоматически определять выходные типы. Например, для выражения

b = 2 + 3

нет необходимости предварительно объявлять b числом.

Язык Haskell поддерживает такие известные типы, как Char (символы Unicode), Float и Double, а также есть тип Ratio для точных рациональных чисел. Рациональные значения создаются с помощью выражения n % m. Для перехода от одного типа к другому для конкретного значения необходимо использовать функции преобразования fromRational и toRational. Для использования этих функций надо вначале импортировать модуль Data.Ratio с помощью следующей команды:

import Data.Ratio

и далее выполнить что-то вроде toRational 1.3.

Аналогично (с помощью модуля Data.Char) можно создать строку в привычном формате “First Test”. Здесь полезно воспользоваться операцией :t для определения типа полученного выражения:

Prelude Data.Char> :t "First Test!" "Hello world!" :: [Char]

Как видно по результату, создан список символов, то есть строки в языке Haskell являются просто списками символов. Вместо [Char] можно использовать имя String. Также можно создавать списки значений другого типа, вводя эти значения в квадратных скобках через запятую (например nn = [1,2,3]). При этом каждый список может состоять только из элементов одного типа. Списки также являются связанными с помощью ссылок на следующие значения, есть привычные функции head и tail. Созданные списки можно объединять с помощью оператора ++. Кроме того, можно использовать операцию :

Пример: добавление значения 1 в список значений [2,3,4,5].

1:[2,3,4,5] [1] ++ [2,3,4,5]

Примечание. Оба параметра оператора ++ должны быть списками, поэтому нельзя написать [1,2,3,4] ++ 5.

С помощью оператора !! можно извлечь элемент из списка по индексу (начиная с нуля), например, выражение [1,2,3,4,5] !! 1 возвращает 2.

2

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

Для списка поддерживаются функции last (последний элемент), init (все, кроме последнего), null (True при пустом списке и False при непустом), length, maximum, minimum, sum, product (произведение), elem (True при присутствии элемента в списке и False при отсутствии), reverse, take (извлекает указанное количество элементов с начала списка), drop (вырезает указанное количество элементов с начала списка).

Отметим, что поддерживаются интервалы типа [1..10] (эквивалент [1,2,3,4,5,6,7,8,9,10]), [2,4..10] (эквивалент [2, 4,6,8,10]; можно указать только один шаг; нельзя задать убывание списком [20..1], надо писать [20,19..1]), ['a'..'z'] (эквивалент "abcdefghijklmnopqrstuvwxyz"). С помощью интервалов можно создать так называемые генераторы списков:

[x*2 | x <– [1..10]] [2,4,6,8,10,12,14,16,18,20]

или посложнее:

[x*2 | x <– [1..10], x*2 >= 12] [12,14,16,18,20]

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

(5, ‘test’, “проверка”)

В функциональной части поддерживаются операции конъюнкции (логическое И) и дизъюнкции (логическое ИЛИ). Для конъюнкции используются символы &&, для дизъюнкции – операция ||, для отрицания – операция not. Например, выражение not False приводит к результату True.

Для сравнения используются операции == и /= (с результатом True или False). Здесь же следует отметить, что поддерживается конструкция

if a then b else c

причем:

b и c должны быть выражениями одного типа;

обязательно должны присутствовать обе ветки (then и else).

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

Выражение succ 8 приведет к результату 9 (следующее число после 8). Выражение max 8 4 приведет к результату 8 (максимум из двух аргументов).

Кроме такой префиксной формы, можно использовать инфиксную форму.

Префиксная форма: div 92 10 Инфиксная форма: 92 ‘div’ 10

К записи с пробелами надо привыкнуть. Например, запись integr (integr 5) означает не вызов функции integr с параметрами integr и 5, а означает:

1.Вызов функции integr с параметром 5.

2.Вызов функции integr с результатом вызова 1 в качестве параметра.

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

cubicValue x = x * x *x

3

Код на языке Haskell можно сохранить в файле с расширением .hs. Для компиляции достаточно набрать команду :l имя_файла. Как уже было отмечено выше, в функциональном языке сами функции можно использовать как аргументы; например, можно определить сумму x^3 + y^3 следующим образом:

cubicXY x y = cubicValue x + cubicValue y

На языке Haskell функцию можно реализовать множеством способов.

Пример: факториал.

Реализация 1

fact x =

if x == 0 then 1

else x * fact (x – 1)

Реализация 2

fact 0 = 1

fact x = x * fact (x – 1)

Реализация 3

fact x = iter 1 1 where iter i res =

if i > x then res

else iter (i + 1) (res * i)

Есть еще способы с использованием свертки, которая в данном курсе не рассматривается.

В последней приведенной реализации используется конструкция where. Переменные, которые определяются в этой конструкции, локальны в рамках функции (видимы только внутри функции).

infoweight :: Double -> String infoweight index

| index <= 18.5 = "Дефицит веса"

| index <= 25.0 = "Нормальный вес" | index <= 30,0 = "Лишний вес"

| otherwise = "Большое отклонение от нормы" where index = weight / height ^ 2

4

Соседние файлы в папке Лекции