Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Функциональное программирование. Конспект лекций

.pdf
Скачиваний:
0
Добавлен:
15.08.2026
Размер:
1 Мб
Скачать

МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ

НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ ЯДЕРНЫЙ УНИВЕРСИТЕТ «МИФИ»

К.Я. Кудрявцев

Функциональное программирование

Конспект лекций

Москва 2020

УДК 004.43 ББК 32.97 К 88

Кудрявцев К.Я. Функциональное программирование: конспект лекций [Электронный ресурс]. М.: НИЯУ МИФИ, 2020. – 112 с.

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

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

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

Конспект лекций создан в рамках Проекта по созданию и развитию Международного научно-методического центра НИЯУ МИФИ.

Рецензент канд. физ.-мат. наук Роганов Е.А.

Рекомендовано кафедрой «Компьютерные системы и технологии» НИЯУ МИФИ

ISBN 978-5-7262-2672-9

© Национальный исследовательский

 

ядерный университет «МИФИ», 2020

 

© К.Я. Кудрявцев, 2020

– 2 –

СОДЕРЖАНИЕ

 

Введение ...........................................................................................................

5

Лекция 1. Функциональный подход к разработке программ..........................

8

1.1. Особенности функционального стиля................................................

8

1.2. Интерпретатор Haskell .......................................................................

10

1.3. Элементы языка Haskell ....................................................................

12

1.4. Ленивые вычисления в Haskell..........................................................

15

Лекция 2. Синтаксис и структура программ..................................................

17

2.1. Типы ....................................................................................................

18

2.1.1. Рекурсивные типы....................................................................

20

2.2. Значения.............................................................................................

21

2.2.1. Локальные выражения.............................................................

23

2.3. Классы типов......................................................................................

25

2.4. Экземпляры классов типов................................................................

25

2.5. Двумерный синтаксис в Haskell.........................................................

26

Лекция 3. Рекурсия .........................................................................................

28

3.1. Рекурсивное вычисление факториала.............................................

28

3.2. Рекурсивное вычисление чисел Фибоначчи....................................

31

3.3. Рекурсивное вычисление числа Эйлера..........................................

33

3.4. Проверка простоты натурального числа..........................................

34

Лекция 4. Работа со списками........................................................................

37

4.1. Конструкторы списков........................................................................

37

4.2. Функции работы со списками ............................................................

39

4.3. Реализация функций работы со списками.......................................

40

4.4. Функция обработки строк...................................................................

43

4.5. Сортировка списка..............................................................................

45

Лекция 5. Функции высших порядков ............................................................

46

5.1. Функция map .......................................................................................

46

5.2. Функции foldl и foldr.............................................................................

48

5.3. Функции filter, zip и zipWith .................................................................

50

5.4. Операции с матрицами......................................................................

52

– 3 –

Лекция 6. Деревья и множества в Hаskell .....................................................

56

6.1. Двоичные деревья..............................................................................

56

6.1.1. Функция вычисления количества листьев в дереве..............

57

6.1.2. Функция разглаживания дерева..............................................

57

6.1.3. Свертка двоичных деревьев ...................................................

60

6.2. Множества...........................................................................................

61

6.2.1. Решето Эратосфена..................................................................

63

Лекция 7. Ввод/вывод в Hаskell .....................................................................

67

7.1. Монада IO ...........................................................................................

68

7.2. do-нотация ..........................................................................................

71

7.3. Работа с файлами..............................................................................

73

Лекция 8. Монады и функторы.......................................................................

77

8.1. Композиция функций..........................................................................

78

8.2. Применение функций.........................................................................

80

8.3. Функторы и монады............................................................................

82

8.4. Mонада State.......................................................................................

83

8.4.1. Пример использования монады State ....................................

88

8.5. Mонада ST...........................................................................................

91

8.5.1. Монада изменяемых массивов STArray .................................

93

Лекция 9. Отладка в Haskell ...........................................................................

97

9.1. Установка точек прерывания.............................................................

97

9.1.1. Вложенные точки прерывания................................................

99

9.1.2. Переменная _result ..................................................................

99

9.2. Пошаговая отладка..........................................................................

100

9.3. Трассировка......................................................................................

101

9.4. Исключения.......................................................................................

103

9.5. Пример отладки................................................................................

104

9.5.1. Пошаговая отладка................................................................

105

9.5.2. Трассировка............................................................................

106

Список литературы.......................................................................................

110

– 4 –

Введение

Функциональное программирование – направление, при кото-

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

В основе традиционного программирования лежит концепция фон Неймана о хранимой программе автоматических вычислений по заданному алгоритму. Существенными чертами «императивной» программы являются:

строгая последовательность в выполнении операторов, возможность хранения и изменения данных в оперативной па-

мяти компьютера.

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

Использование многопроцессорных систем не приводит к существенному увеличению производительности, так как программы, написанные на традиционных «императивных» языках программирования, предполагают последовательное выполнение команд.

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

– 5 –

отдельных частей алгоритма. Языки, в которых последовательность выполнения отдельных частей программы является несущественной, носят название декларативных. Программист при написании программы сосредоточен не на том, как достичь нужного результата (в какой последовательности выполнять шаги алгоритма), а на том, что должен представлять собой этот результат, т.е. на описании (декларировании) требований к программе. Одним из первых декларативных языков программирования был Lisp.

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

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

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

В настоящее время наиболее популярным языком для функционального программирования является Haskell [2], история которого начинается в 1987 г. Он был назван в честь одного из основателей комбинаторной логики Хаскеля Кэрри (Haskell Curry). Это свободный язык, прпердназначенный для исследовательской деятельности и решения практических задач. Первая версия стандарта Haskell была опубликована 1 апреля 1990 г. Haskell продолжает развиваться, и сегодня существуют два стандарта: 1998 и 2010 г. Интерес к Haskell вызван популярностью многопроцессорных технологий. Модель вычислений Haskell хорошо подходит для распараллеливания.

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

Данный курс посвящен изучению функционального подхода к разработке программ на языке Hаskell, построен на замечательной книге Филда и Харрисона «Функциональное программирование»

[1]и носит практический характер. Строгое изложение синтаксиса

6 –

языка, а также материал по функторам и монадам основаны на прекрасной книге Антона Холомьева «Учебник по Haskell» [2]. Некоторые примеры можно найти в учебнике Н.А. Рогановой «Функциональное программирование» [3]. Официальным сайтом языка

Haskell является https://www.haskell.org [4]. Очень интересные при-

меры (но довольно сложные) приведены в книге Р.В. Душкина «14 занимательных эссе о языке Haskell и функциональном программировании» [5]. Имеется справочник на русском языке – Р.В. Душкин «Справочник по языку Haskell» [6]. Кроме приведенных источников на просторах интернета можно найти множество примеров и методических материалов по функциональному программированию.

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

Фрагменты кода программ на языке Haskell будут представлены в прямоугольнике, например:

*TestTrace> :list qsort

4

5qsort [] = []

6qsort (a:as) = trace ("left:" ++ show left) (qsort left) ++ [a] ++ trace ("right:" ++ show right) (qsort right)

7where (left,right) = (filter (<=a) as, filter (>a) as)

– 7 –

Лекция 1. Функциональный подход к разработке программ

Рассмотрены особенности функционального подхода при разработке программ. Описаны способы компиляции и запуска программ. Рассмотрены основные элементы языка Haskell и понятия ленивых вычислений. Приведены примеры простых программ на языке Haskell.

Ключевые слова: функциональный стиль; типы данных; интерпретатор Haskell.

Введение. В данной лекции будут рассмотрены особенности функционального подхода при разработке программ. Описаны особенности функционального стиля программирования. Будет рассмотрен интерпретатор языка Haskell, его реализация для ОС MS Windows, а также способы компиляции и запуска программ. Рассмотрены основные элементы языка Haskell, понятия ленивых вычислений. Приведены примеры простых программ на языке

Haskell.

1.1. Особенности функционального стиля

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

Функции, определяемые в языке функционального программирования Haskell, выдают один и тот же результат на одном и том же наборе аргументов, т.е. результат выполнения функции является «повторяемым». Вычисление функции не

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

– 8 –

Если программа представляет собой набор чистых детерминированных функций, то она будет «функциональной» независимо от того, на каком языке она написана (императивном или функциональном). Ниже приведена функция вычисления количества вещественных корней заданного квадратного уравнения. Функция получает в качестве исходных данных коэффициенты квадратного уравнения, вычисляет его дискриминант, а затем формирует нужный результат в зависимости от знака дискриминанта:

int roots(double a, double b, double c) { double discr = b * b - 4 * a * c;

if (discr < 0) return 0; else if (discr == 0) return 1; else return 2;

}

Это функция roots, детерминированная и «чистая», однако, с точки зрения функционального стиля, она «неправильная». В функции определяется и используется локальная переменная discr для запоминания значения дискриминанта квадратного уравнения. Это никак не влияет на «чистоту» написанной функции, но если бы внутри функции были определены другие функции, то результат их работы мог быть различным в зависимости от того, когда они вызываются – до присваивания переменной discr нового значения или после него.

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

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

– 9 –

double discr (double a, double b, double c) { return b * b - 4 * a * c;

}

int sign (double x) {

return x < 0 ? -1 : x == 0 ? 0 : 1;

}

int roots(double a, double b, double c) { return sign(discr(a, b, c)) + 1;

}

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

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

Альтернатива циклам – рекурсивные функции, которые являются основополагающими при написании функциональных программ.

1.2. Интерпретатор Haskell

Язык Haskell является свободно распространяемым ПО [4]. Существуют версии языка под разные платформы. В дальнейшем работа с Haskell будет производиться на платформе MS Windows 7.

Для подготовки кода программы можно пользоваться любым тестовым редактором, например Notepad++, который целесообразно настроить на отображения синтаксиса Haskell.

Простейшая программа, которая находится в файле helloWorld.hs, для вывода строки «Hello World» выглядит следующим образом:

main = putStrLn "Hello, World!"

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

– 10 –

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]