Урок 1. Что такое функциональное программирование

19
ФУНКЦИОНАЛЬНОЕ ПРОГРАММИРОВАНИЕ

Upload: -mydls

Post on 07-Aug-2015

235 views

Category:

Education


2 download

TRANSCRIPT

Page 1: Урок 1. Что такое функциональное программирование

ФУНКЦИОНАЛЬНОЕ ПРОГРАММИРОВАНИЕ

Page 2: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Глава 1. Элементы функционального программирования

Архитектура фон Неймана диктует стиль программирования?

Средства программирования: Арифметические и логические операции;

Присваивания;

Последовательное исполнение шагов алгоритма;

Управление (управляющие конструкции);

Процедуры и функции;

Модули, исключительные ситуации, структуры данных,...

Программа: описание процесса (алгоритма) или «черный ящик»?

Действие

Выбор

Если «да» Если «нет»

Алгоритм

«Черный ящик»Вход Выход

Функция

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

Page 3: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Задача о вычислении значений квадратных корней уравнения(процедурный стиль программирования)

/** Процедура вычисляет вещественные или комплексные корни квадратного трехчлена, * в предположении, что первый коэффициент (a) отличен от нуля. * Аргументы: a, b, c – коэффициенты квадратного трехчлена; * Результаты: complexFlag – признак комплексных корней; * r1, r2 – вещественные корни, если complexFlag = False и * вещественная и мнимая части двух корней, если complexFlag = True */

class Result { boolean complexFlag; double r1; double r2; }

static double discriminant (double a, double b, double c) { return b * b – 4 * a * c;}

static Result squareRoots (double a, double b, double c) { Result result = new Result(); double discr = discriminant (a, b, c); // Вычисляем дискриминант result.complexFlag = discr < 0; // Определяем, вещественные или мнимые корни if (result.complexFlag) { result.r1 = (-b) / (2*a); // Вещественная часть корней result.r2 = Math.sqrt(-discr) / (2*a); // Мнимая часть корней } else { result.r1 = (-b + Math.sqrt(discr)) / (2*a); // Первый вещественный корень result.r2 = (-b – Math.sqrt(discr)) / (2*a); // Второй вещественный корень } return result;}

Page 4: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Та же программа, написанная в функциональном стиле программирования(на псевдоязыке с Java-подобным синтаксисом)

/** Функция вычисляет вещественные или комплексные корни квадратного трехчлена, * в предположении, что первый коэффициент (a) отличен от нуля. * Аргументы: a, b, c – коэффициенты квадратного трехчлена; * Результаты: признак комплексных корней; * вещественные корни, если они вещественные, и * вещественная и мнимая части двух корней, если мнимые */double discriminant (double a, double b, double c) { return b * b – 4 * a * c;}

(boolean, double, double) squareRoots (double a, double b, double c) { final double discr = discriminant(a, b, c); // Значение дискриминанта final double complexFlag = discr < 0; // Определяем, вещественные или мнимые корни (complexFlag, (complexFlag ? ((-b) / (2*a), sqrt(-discr) / (2*a)) : ((-b + sqrt(discr)) / (2*a), (-b – sqrt(discr)) / (2*a)) );}

Page 5: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Особенности этой программы

Вместо переменных и присваиваний

используются константы

Составные значения легко описываются...

...и создаются

Широко используются условные выражения

Константы получают динамически

вычисляемые значения

Тело функции представляет собой суперпозицию применений функций для описания функциональной зависимости

результата от входных данных

Результаты не зависят от порядка вычислений

(возможны параллельные вычисления)

double discriminant (double a, double b, double c) { return b * b – 4 * a * c;}

(boolean, double, double) squareRoots (double a, double b, double c) { final double discr = discriminant(a, b, c); // Значение дискриминанта final double complexFlag = discr < 0; // Определяем, вещественные или мнимые корни (complexFlag, (complexFlag ? ((-b) / (2*a), sqrt(-discr) / (2*a)) : ((-b + sqrt(discr)) / (2*a), (-b – sqrt(discr)) / (2*a)) );}

Page 6: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

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

boolean intSet (int); // описание функционального типа данных

boolean emptySet (int n) { return false; } // пустое множество

boolean oddSet (int n) { return n % 2 == 1; } // множество нечетных чисел

Несколько полезных операций над множествами

intSet addElement (intSet s; int newElem) { boolean newSet (int n) { return s(n) || (n == newElem); } return newSet;}

intSet buildInterval (int min, int max) { boolean newSet (int n) { return (n >= min) && (n <= max); } return newSet;}

intSet difference (intSet s1, intSet s2) { boolean newSet (int n) { return s1(n) && ! s2(n); } return newSet;}

Будут ли работать эти операции?

Page 7: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Попробуем вычислить выражение

difference (oddSet, addElement (emptySet, 3)) (7) { Принадлежит ли 7 множеству [odds] \ [3] }

addElements = emptySet

newElem = 3

boolean emptySet (int n) { return false; }

intSet addElement (intSet s, int newElem) { boolean newSet (int n) { return s(n) || (n == newElem); } return newSet;}

boolean oddSet (int n) { return n % 2 == 1; }

intSet difference (intSet s1, intSet s2) { boolean newSet (int n) { return s1(n) && ! s2(n); } return newSet; }

Стек вычислений

newSet

s2 = addElement.newSet

s1 = oddSet

difference

newSet

Page 8: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Функциональное представление множеств в языке Java

В языке Java все же можно построить правильную программу для работы с функциональным представлением множеств:

abstract class IntSet { abstract boolean has(int elem); public static IntSet addElement(final IntSet s, final int n) { return new IntSet() { public boolean has(int elem) { return elem == n || s.has(elem); } }; } public static IntSet buildInterval(final int n, final int m) { return new IntSet() { public boolean has(int elem) { return elem >= n && elem <= m; } }; } public static IntSet difference(final IntSet s1, final IntSet s2) { return new IntSet() { public boolean has(int elem) { return s1.has(elem) && ! s2.has(elem); } }; } public static void main(String[] args) { IntSet emptySet = new IntSet() { public boolean has(int e) { return false; }}; IntSet oddSet = new IntSet() { public boolean has(int e) { return e % 2 == 1; }}; System.out.println(difference(oddSet, addElement(emptySet, 3)).has(7)); }}

Page 9: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Подведение итогов

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

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

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

Традиционные языки не могут обеспечить удобных средств для распараллеливания вычислений: последовательное выполнение команд – узкое место традиционной архитектуры компьютеров («фон-Неймановское горлышко»).

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

Page 10: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Литература

Основная

Дополнительная

1. А. Филд, П. Харрисон. Функциональное программирование. М.: Мир, 1993.2. П. Хендерсон. Функциональное программирование. Применение и реализация.

М.: Мир, 1983.3. А.А.Кубенский. Функциональное программирование. ИТМО, 2010.

1. Пол Хьюдак, Джон Петерсон, Джозеф Фасел. Мягкое введение в Haskell.RSDN Magazin, 2006 № 4 и 2007 № 1(http://rsdn.ru/article/haskell/haskell_part1.xml, http://rsdn.ru/article/haskell/haskell_part2.xml)

2. Р. В. Душкин Функциональное программирование на языке Haskell (+ CD-ROM)Издательство: ДМК Пресс, 2007 г., Мягкая обложка, 608 стр.

3. Н.А.Роганова. Функциональное программирование. Учебник для вузов, 2002.4. Graham Hutton. Programming in Haskell. 2007 г., Мягкая обложка, 184 стр. 5. Bryan O'Sullivan, John Goerzen, Donald Stewart. Real World Haskell:

Code You Can Believe In. Издательство: O'Reilly Media, 2008 г., Мягкая обложка, 710 стр.

Page 11: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

1.2. Введение в язык Haskell

История языка Haskell

1960: John McCarthy, LISP – первый функциональный язык программирования

1978: John Backus, FP – система комбинаторного программирования

конец 1970-х: Edinburgh univ., ML – meta-language

1985-1986: David Turner, Miranda – функциональный язык с «ленивыми» вычислениями

начало 1930-х: Church, формализация функций в λ-исчислении

1990: Ericsson, Erlang – «коммерческий» функциональный язык

1988: Paul Hudak, Haskell – первая версия языка Haskell

1999: Haskell group, Haskell’98 – «стандартная» версия языка Haskell

Haskell – чисто функциональный язык программирования, названный в честь Хаскелла Карри (Haskell B. Curry – 1900-1982), известного, главным образом, благодаря работам в области математической логики и комбинаторной логики в конце 1950-х – начале 1960-х годов

http://haskell.org/onlinereport/index.html - пересмотренное сообщение о языке Haskell’98http://haskell.org/tutorial - «Нежное» введение в Haskell

Page 12: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Типы данных и базовые конструкции языка Haskell

Элементарные типы данных

Integer, Int – целые значения (25, -17, 111222333444555666777888)

Float, Double – вещественные значения (3.14, -2.718281828459045)

Char – символьные значения ('A', '*', '3')

Bool – логические значения (True, False)

Идентификаторы: fact, fAcToRiAl, fact_1, fact''

Знаки операций: +, -, *, <, ==

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

school :: Integerschool = 239

piHalf :: DoublepiHalf = 3.1415926536 / 2

Page 13: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Кортежи и функции

Значения могут объединяться в более сложные с помощью кортежирования.

Например:

pair :: (Double, Double)pair = (2.7, 3.14)

attributes :: (Char, (Int, Int, Int), Bool)attributes = ('M', (17, 4, 1955), True)

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

sin :: Double -> Double -- аргумент и результат типа DoubleplusInt :: Int -> Int -> Int -- два аргумента типа Int, результат IntdivMod :: (Int, Int) -> (Int, Int) -- аргумент и результат - кортежиВыражения составляются из констант применением операций и функций, например:

result = sin (3.1416 / 4) - 2.5c10 = 3 + plusInt 3 4pair = divMod (1458, plusInt 176 192)

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

3 + 8 и (+) 3 827 `div` 4 и div 27 47 `plusInt` 11 и plusInt 7 11

Page 14: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Определение функций с помощью уравнений

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

plusInt :: Int -> Int -> IntplusInt a b = a + b

divMod :: (Int, Int) -> (Int, Int)divMod (a, b) = (a `div` b, a `mod` b)

Уравнения могут содержать условные выражения и рекурсивные обращения, например:

factorial :: Integer -> Integerfactorial n = if n == 0 then 1 else n * (factorial (n-1))

sum :: Integer -> Integersum n = n + if n == 0 then 0 else sum (n-1)

factorial1 :: Integer -> Integerfactorial1 n | n == 0 = 1 | n > 0 = n * (factorial1 (n-1))

factorial2 :: Integer -> Integerfactorial2 0 = 1 factorial2 n = n * (factorial2 (n-1))

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

Page 15: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Подготовка и запуск программ

factorial :: Integer -> Integer factorial n | n == 0 = 1 | n > 0 = n * (factorial (n-1))

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

- ошибка

factorial :: Integer -> Integerfactorial n | n == 0 = 1 | n > 0 = n * (factorial (n-1))

- ошибка

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

factorial :: Integer -> Integerfactorial n | n == 0 = 1 | n > 0 = n * factorial (n-1))

- нет ошибки

Page 16: Урок 1. Что такое функциональное программирование

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

Глава 1. Элементы функционального программирования.

Подготовка и запуск программ

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

{ factorial :: Integer -> Integer; factorial n | n == 0 = 1 | n > 0 =n * (factorial (n-1)) }

- нет ошибки

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

• анализ начинается в режиме полного синтаксиса;• если там, где нужна открывающая фигурная скобка, ее нет, то она автоматически

вставляется, а анализатор переходит в режим краткого синтаксиса;• если в режиме краткого синтаксиса очередная строка начинается с той же позиции, что и

начало всего предложения, то перед ней автоматически вставляется точка с запятой;• если очередная строка начинается с отступом влево от начала текущего предложения, то

вставляется закрывающая фигурная скобка;• если очередная строка начинается с отступом вправо, то это – продолжение предыдущей

строки (ничего не вставляется)

Page 17: Урок 1. Что такое функциональное программирование

17

Исполнение программ с помощью текстовой подстановки

factorial :: Integer -> Integerfactorial n | n == 0 = 1 | n > 0 = n * (factorial (n-1))

factorial 33 * (factorial (3-1))3 * (factorial 2)3 * (2 * (factorial (2-1)))3 * (2 * (factorial 1))3 * (2 * (1 * (factorial (1-1))))3 * (2 * (1 * (factorial 0)))3 * (2 * (1 * 1))6

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

Глава 1. Элементы функционального программирования.

Page 18: Урок 1. Что такое функциональное программирование

18

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

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

Глава 1. Элементы функционального программирования.

-- Вычисление наибольшего общего делителя двух натуральных чиселgcd :: Integer -> Integer -> Integer gcd m n | m < n = gcd n m | n < 0 = error "gcd: Wrong argument" gcd m 0 = m

gcd m n = gcd n (m `mod` n)

-- Проверка заданного натурального числа на простотуprime :: Integer -> Bool prime' :: Integer -> Integer -> Bool prime p | p <= 0 = error "prime: Non-positive argument" | otherwise = prime' 2 p prime' d p | d * d > p = True | p `mod` d == 0 = False

| otherwise = prime' (d+1) p

Page 19: Урок 1. Что такое функциональное программирование

19

Немного о явных и неявных преобразованиях типов

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

Глава 1. Элементы функционального программирования.

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

2 + 5 – сложение целых;

2.5 + 3.5 – сложение вещественных;

2 + 3.5 – сложение вещественных (первый операнд перед операцией преобразуется);

n + 3.5 where n = 2 – сложение вещественных (тип первого операнда «выводится» как вещественное);

fromIntegral n + 3.5 where { n :: Int; n = 2 } – сложение вещественных (тип первого операнда явно преобразован);

a + b where { a :: Int; a = 2; b :: Integer; b = 12 } – ошибка (операнды имеют разные типы);

n + 3.5 where { n :: Int; n = 2 } – ошибка (тип первого операнда явно указан как целый);

a + fromIntegral b where { a :: Int; a = 2; b :: Integer; b = 12 } – сложение коротких целых;

Некоторые функции преобразования типов:• fromIntegral, fromRational, fromEnum,• truncate, round, ceiling, floor