Вы находитесь на странице: 1из 20

Лекция 1.

Введение и Обзор

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

Джон Харрисон

Университет Кембриджа

10 сентября 2008 г.

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Темы

Императивное программирование
Функциональное программирование
Достоинства функционального программирования
Обзор курса
λ-нотация и её достоинства

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Императивное Программирование

Императивные (или процедурные) программы основываются на


изменении состояния с помощью последовательности выполняемых
команд.
Состояния в основном изменяются при помощи команд
присваивания, записываемых как v = E или v := E.
Мы можем выполнить одну команду за другой, написав их
последовательно, возможно разделяя точкой с запятой: C1 ; C2 .
Команды выполняются условно при помощи if, и циклично при
помощи while.
Программа — последовательность инструкций по изменению
состояния.
Императивные языки программирования, такие как FORTRAN,
Algol, C, Modula-3, поддерживают такой стиль программирования.

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Абстрактный вид

Забудем про операции ввода-вывода и допустим, что программа


работает конечное время, производя какой-либо результат.
Рассмотрим выполнение в абстрактном виде:

σ0 → σ1 → σ2 → · · · → σn

Программа начинает работу с начального состояния σ0 ,


содержащего входные значения для программы.
Программа заканчивает работу в конечном состоянии σn ,
содержащем результат(ы) работы программы.
Состояние изменяется конечное число раз из σ0 в σn ; в принципе,
каждая команда может изменить состояние.

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

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

Функциональная программа - это просто выражение, а процесс


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

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

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

Мы можем записать функцию для вычисления факториала в


императивном стиле на языке C следующим образом:
int fact ( int n)
{ int x = 1;
while ( n > 0)
{ x = x ∗ n;
n = n − 1;
}
return x ;
}

тогда как на ML это можно записать в виде рекурсивной функциий:


l e t rec f a c t n =
i f n = 0 then 1
else n ∗ fact (n − 1 ) ; ;

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Зачем?

На первый взгляд язык без переменных, присваивания и


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

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Достоинства функционального программирования

Избегая использования переменных и операций присваивания, мы


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

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Денотационная семантика

Функцию факториала на ML мы можем представить как


математическую (частичную) функцию Z → Z:

n! if n ≥ 0
[[fact]](n) =
⊥ otherwise
где ⊥ – неопределённость.
Если у нас появляется состояние, то такое представление не
работает. Вот «функция» на C, которой не соответствует ни одна
математическая функция:
i n t rand ( void )
{ s t a t i c int n = 0;
r e t u r n n = 2147001325 ∗ n + 7 1 5 1 3 6 3 0 5 ;
}

Она выдаёт различные значения при успешных вызовах!

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Семантика императивных программ

Чтобы создать надлежащую семантику для императивных программ,


нам необходимо явно определить состояние. Например, мы можем
создавать команды как:
Частичные функции Σ → Σ (Strachey)
Отношения Σ × Σ (Hoare)
Предикатные преобразователи, то есть всюду определённые
функции (Σ → bool ) → (Σ → bool ) (Dijkstra)
Даже если позволить существование инструкции goto, то этого
все-равно мало и нам нужна семантика, основанная на
продолжениях (Wadsworth, Morris).
Все эти методы достаточно сложны.
В функциональных программах у нас есть возможность проверить
их корректность, или корректность некоторых преобразований и
оптимизаций.

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Проблемы с функциональными программами

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


Некоторые вещи сложнее внедрить в чисто функциональную модель,
например:
Ввод-вывод
Интерактивные или постоянно запущенные программы
(редакторы и др.)
Зачастую, чтобы добиться всего этого, можно использовать
бесконечные структуры данных.
Функциональные языки также в меньшей степени соответствуют
современному аппаратному обеспечению, поэтому функциональные
программы могут быть менее эффективными, а оценка времени
исполнения и потребляемых ресурсов — непростой.
ML не чисто функциональный язык, поэтому вы можете
использовать присваивание переменных, если это потребуется.
Большинство наших программ написано на чисто функциональном
подмножестве языка.

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Обзор курса (1)

Мы начнём с теоретического фундамента — λ-исчисления, и


дойдём до практического применения программирования на ML.
Ориентировочно треть курса посвящена теории и две трети практике.
Теоретические части:
1 Введение и обзор (эта лекция)
2 λ-исчисление как формальная система
3 λ-исчисление как язык программирования
4 Типы

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Обзор курса (2)

Практические или «прикладные» части курса:


1 Введение в ML
2 Разбор полиморфизма
3 Рекурсивные типы
4 Доказательства программ
5 Продвинутый ML
6 Другие стили функционального программирования
7 Примеры на ML (1)
8 Примеры на ML (2)

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

λ-нотация

λ-нотация – это способ определения функций, предложенный Алонзо


Чёрчем в 1930 году. Мы пишем:

λx. E [x]

чтобы определить «функцию от x, которая возвращает E [x]». Здесь


E [x] – это выражение, которое содержит или не содержит x.
Например, λx. x – это функция тождественного отображения,
которая возвращает заданный аргумент, а λx. x 2 – это функция
возведения в квадрат.
Литера λ – историческая случайность. Первоначально Чёрч
использовал обозначение x̂. E [x], а серия ошибок набора превратила
его в нынешнюю форму. Иногда также пишут x 7→ E [x] и [x] E [x].

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Зачем?

В неформальной математике, когда мы говорим о функции, то


обычно даём ей какое-то имя: «Пусть f (x) = E [x] . . . тогда · · · f · · · ».
Большинство языков программирования позволяют определить
функцию только если ей будет дано имя.
Такой подход не совсем оправдан. Он означает, что мы не
рассматриваем функции наравне с другими математическими
объектами. Мы не говорим:
Определим x и y как x = 2 и y = 4 соответственно. Тогда
xx = y
λ-нотация помогает привести функции в равное положение. Это
является важным аспектом функционального программирования.

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Преимущества λ-нотации

λ-нотация также может быть полезна для улучшения ясности


математического изложения.
Когда мы говорим x + xy , то часто не разъясняем, является ли это
выражение определённым значением для конкретного x, или это
функция от x (или y . . . ). Используя λ-нотацию мы можем явно это
обозначить.
Вообще, используя только:
Переменные, например, x, y .
Константы, например, 3, true, +.
Применение функции к аргументам, например, f x.
λ-абстракцию выражения над переменными, например λx. x + y
мы составим основной «абстрактный синтаксис» математики.

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Каррирование

У нас могут быть функции от двух и более аргументов, например


λx. λy . x + y .
Мы можем думать о ней как о функции одного аргумента
Z → (Z → Z). Если дан аргумент, допустим 2, то она возвращает
функцию одного аргумента Z → Z, которая прибавляет 2 к своему
аргументу. Этот принцип известен как каррирование, в честь
Хаскелла Карри.
Примем соглашение, что λx y . E [x, y ] означает λx. λy . E [x, y ], и что
функция применяется всегда к левому аргументу, например f x y
означает (f (x))(y ). Тогда имеет место принцип каррирования,
например

(λx y . x + y ) 1 2 = (λy . 1 + y ) 2 = 1 + 2

Заметим, что скобки необязательны.

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Связывание переменных

Многие конструкции в математике связывают переменные.


Переменная x в
Z a
3x 2 dx
0

связана, тогда как a свободна; также n связана, а k свободна в

Σkn=0 n2

Мы увидим, что свободные и связанные переменные могут быть


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

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Пример дифференцирования
d 2
Общепринятая конструкция dx x может быть разложена как:

D (λx. EXP x 2) x

где D (каррированный) оператор дифференцирования


(R → R) → R → R, возвращающий производную его первого аргумента
в точке, заданной вторым аргументом, а EXP - функция возведения в
степень.
Получается, что тут две переменные x, одна связана, а другая нет.
Аналогично в
Z x
2x dx
0

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

Джон Харрисон Введение в Функциональное программирование


Лекция 1. Введение и Обзор

Парадокс Рассела

Изначально Чёрч надеялся использовать λ-нотацию как основу


математики, введя константы для логических операций, например
чтобы ¬ означало «не». К сожалению, у него не получилось, так как
определяя R = λx. ¬ (x x), мы получаем:

R R = (λx. ¬ (x x)) R = ¬ (R R)

Как мы знаем, существует соответствие между множествами и их


характеристическими предикатами, поэтому, если рассматривать s x
как x ∈ s, то получается хорошо известный парадокс Рассела о
множестве всех множеств, не содержащих себя.

R = {x | x 6∈ x}

Джон Харрисон Введение в Функциональное программирование

Вам также может понравиться