480 likes | 751 Views
Функциональное программирование. Функциональное программирование. Симуни Михаил Лазаревич msimuni.wikidot.com/fp группа ВКонтакте “ Функциональное программирование (мат-мех 14 ) ” simuni@mail.ru. Что надо будет для зачета?. Набрать >= 20 баллов Контрольная в середине курса
E N D
Функциональное программирование
Функциональное программирование Симуни Михаил Лазаревич msimuni.wikidot.com/fp группа ВКонтакте “Функциональное программирование (мат-мех 14)” simuni@mail.ru
Что надо будет для зачета? • Набрать >= 20 баллов • Контрольная в середине курса • Зачет: задачи + теор.вопросы • Больше баллов – льготы на зачете 30 – минус 1 задача40 – минус 2 задачи50 – минус 3 задачии т.д.
Откуда берутся баллы Задачи на дом (примерно по 4 задачи на занятии) И еще будут дополнительные задачи посложнее MOOC ? Scala – Martin Odersky Functional programming - Erick Mejer если кто-то прослушает какой-нибудь курс и покажет сертификат – 30 баллов Или если кто-то слушал ФП в Computer Science Center и может это продемонстрировать – тоже 30 баллов 4
Про задачи Как сдавать задачи: Система тестированияhttp://ms8255.ru/fp Или можно присылать по почте, или приносить на листке Правила про задачи: Надо сдавать не позже начала пары Можно писать не (совсем) правильно Нельзя присылать очень похожие решения… 5
На чем писать? • Я пишу на Haskell • Если есть желающие, можно F#, LISP (Clojure, Racket и т.д.), ML (OCaml), Scala, Erlangи т.д. (почти для всех задач) • А м.б. и Питон, Ruby и т.д. • Компиляторы – Haskell Platform, WinHugs, FP Haskell Center • На сайте документы про то, как начать работать с WinHugsи Haskell Platform
Литература • Литература • См. ссылки на сайте • Роганова Н.А. «Функциональное программирование» – основы, очень просто • «Изучай Хаскел ради добра» • Г.Макеев «Основы функционального программирования на языке Haskell»
Кто придумал ФП? Alonzo Chirch • Примерно 1932 г. до первого компьютера! (1941, К.Цузе) Что такое алгоритм? Каким минимальным набором средств, можно описать любой алгоритм? Ответы: • Тьюринг: Машина Тьюринга • Черч:Лямбда-исчисление: Функции!
Лисп John McCarthy • 1958 г. LISPЯзык высокого уровня #2! (После Фортрана) • Маленький и простой • Не используется GOTO и т.д. • Сборка мусора • Программы можно использовать, как данные
Haskell и потом • Scheme, ML, OCAML … • 1990 – Haskell • C# 3.0 • F# • C++11 • Java 8 • Erlang • Scala • Python, Ruby и т.д.
Что такое ФП? Wikipedia:“programming paradigm, that treats computation as the evaluation of mathematical functions and avoids state and mutable data.” • Т.е. мы себя ограничиваем • Не используем оператор присваивания • Все переменные const • Не используем for и т.д. • А.П.Ершов: Примерно как играть в волейбол с одной привязанной рукой • Зачем? • Меньше ошибок • Проще оптимизировать • Проще выполнять параллельно • И т.д. Убедительно? • John Hughes «Why Functional Programming Matters»«Если бы исключение оператора присваивания принесло такие огромные выгоды, то программисты на Фортране сделали бы это лет двадцать назад».
Функции высшего порядка • Функция, параметр которой – тоже функция. Например: • integral(0, 1, sin) • draw(0, 1, cos) • forall(list, print); • Можно зайти очень далеко (и мы постараемся): • Функции, как результат функции • Сложные случаи (функция, которая возвращает функцию, которая возвращает функцию и т.д.) • Например, одна из функций, которую придумал Черч:λn.λf.λx. n (λg.λh. h (g f)) (λu. x) (λu. u)
Ленивые вычисления, бесконечные списки • x = sin(y); идея: может быть, тут не вычислять ничего… … cout << x; … ивот только тут вычислить sin • И тоже постараемся далеко зайти… • Бесконечные списки Как найти n-ное число Фибоначчи? • Описать бесконечный список, в котором каждый элемент равен сумме двух предыдущих • Взять в этомсписке n-ный элемент
Система типов Вывод типов • C#:var x = Math.Sin(1); • Не надо писать тип x – компилятор и сам может догадаться • И, опять же, попробуем далеко зайти) • Вообще не пишем типы (ну, почти совсем…). Но они есть – компилятор выводит их сам. Полиморфизм • То, что в С++ хотели ввести под именем concept (но не очень получилось)
Фокусы • continuations • «вызвать функцию, апотом …» • Монады • Один из примеров: Выполняем последовательность действий до первой ошибки • М.б. еще что-то (lens, arrows)??
Теория • Лямбда исчисление • Изоморфизм Карри-Ховарда • М.б. еще что-то?? (free theorems?)
Это все действительно полезно знать? • Будете ли вы писать на Хаскеле? • Скорее всего, нет • Будете ли вы использовать приемы ФП в обычных языках? • Скорее всего, да • М.б. полезные приемы • Просто как пример, Map/Reduce • Развивает сообразительность…
Лексика • Комментарии -- это комментарий {-- Аэто многострочный комментарий --} • Имена • Первая буква имеет значение • x, sin – переменные, функции • Integer, True- имена типов, константы • Можно использовать кавычки • abc’
Числа Как обычно: 563.1415926 3.0e9 • М.б. бесконечнозначные 2^100 2^1000 Операции как обычно: • + - * / • Для деления нацело с остатком функции div, mod • ^ - возведение в степень 2^1000 = 1071508607186267320948425049060001810561404811705533607443750388370351051124936122493198378815695858127594672917553146825187145285692314043598457757469857480394567774824230985421074605062371141877954182153046474983581941 267398767559165543946077062914571196477686542167660429831652624386837205668069376
Логические значения • Сравнение • Не равно: /= • Остальное как обычно: < <= > >= == • True, False • && || not Пример:x*y < 0 && (y*y + x*x < 1)
Функции Не совсем обычно! • Не пишутся ни запятые, ни скобкиsin xmod k 10 + div k 10 • Приоритет: сначала функции, потом операторы sin x*x– это(sin x) * x Совет:Непонятная ошибка проверьте, правильно ли расставлены скобки • f g x – это (f g) x Т.е. скобки группируются слева направо. • sin sin 1 – это (sin sin) 1 (ошибка) • А когда это удобно? Позже обсудим…
Как определить свою функцию • Самый простой пример: sqr x = x*x Потом: sqr 5 • Пример простой рекурсивной функции (Конечно же, это факториал:) fact 1 = 1 fact n = fact (n-1) * n • Порядок важен! fact n = fact (n-1) * n fact 1 = 1 // Досюда не дойдет:( • Одно ограничение, на первый взгляд непонятное: f n n = … // Так нельзя! • Переменная слева от = не может встречаться 2 раза (называется: линейность)
Условный оператор.Где начинается следующее правило? • if условие then выражение else выражение abs x = if x > 0 then x else –x f x = x + if x > 0 then x else 0 • Откуда компилятор знает, где начинается следующее правило? • С первой позиции в строке новое правило • Строка начинается с пробела продолжение правила
Операторы • Можно определять свои операторы i @@ j = i*i + j*j • Пример вызова 5 @@ 7 • Может использоваться любая последовательность символов (кроме зарезервированных типа -> и ::) • Обычные функции можно использовать, как инфиксный оператор • Надо взять в обратные кавычки (`) i `mod` 100
Хвостовая рекурсияНакапливающие параметры
Как вычисляется факториал fact 1 = 1 fact n = fact (n-1) * n fact 5 = fact 4 * 5 fact 4 = fact 3 * 4 fact 3 = fact 2 * 3 fact 2 = fact 1 * 2 fact 1 = 1 • Для fact 1000000 на стеке будет 1000000 вызовов 120 24 6 2 1
Еще вариант факториала • Давайте сразу перемножать во время рекурсии fact n = fact’ n 1 fact' 1 p = p fact' np = fact’ (n-1) (n*p) fact' 5 1 = fact’ 4 (5*1) fact'4 5 = fact’ 3 (4*5) fact’ 3 20 = fact’ 2 (3*20) fact’ 2 60 = fact’ 1 (2*60) fact’ 1 120 =120 fact n = fact’ n 1 fact' n p = fact’ (n-1) n*p fact' 1 p = p • Две ошибки 5 1 4 5 3 20 2 60 1 120
Хвостовая рекурсия • Если вызов функции – это последнее, что происходит при вычислении правило, то он называется хвостовым вызовом (tail call). • Если в некоторой функции все рекурсивные вызовы – хвостовые, то говорят, что в этой функции используется хвостовая рекурсия (tail recursion) • Студент X: пример хвостовой рекурсии:fact n = n * fact (n-1) • Нет! • Зачем нужна хвостовая рекурсия? • Можно реализовать более эффективно (и во многих языках так и реализовано) • Но не в Haskell… • Иногда просто удобнее писать (накапливающие параметры).
Накапливающие параметры fact' 1 p = p fact' np = fact’ (n-1) (n*p) • Функция использует p, чтобы передавать в рекурсивный вызов информацию о частично вычисленном значении. • Называется накапливающие параметры (accumulating parameters) Еще пример: • sumprod n = 1 + 2 + … + n / 1*2*3*…*n sumprod n = sumprod’ n 0 1 sumprod' 0 s p = s / p sumprod' n s p = sumprod’ (n-1) (s+n) (p*n)
Списки • Основной тип данных • (и почти во всех функциональных языках) • Примеры: [1, 2, 3] [3.5, 2.123, 98.14] [True, False] • Элементы д.б. одного типа! [1, True, 2] -- Ошибка!
Как создавать списки Оператор : • Оператор : • Приписать элемент в начало 1 : [2,3] [1,2,3] • Пример: f 0 = [] f n = n : f (n-1) • Список чисел от n до 1 • Кстати:[a..b] – числа от a до b с шагом 1 • На самом деле, [1,2,3] – это просто сокращение! • [1, 2, 3] – сокращение для 1:2:3:[] • Так и хранится: : / \ 1 : / \ 2 : / \ 3 []
Как обрабатывать списки – способ 1 • head – первый элемент списка • tail – все,кроме первого элемента («хвост») head [1,2,3] 1 tail [1,2,3] [2,3] • Пример: сумма элементов списка sum [] = 0 sum xs = head xs + sum(tail xs) Замечания: • Типичные имена для списков в Haskel: xs, ysи т.д. • sum – на самом деле, есть такая стандартная функция
ШаблоныКак обрабатывать списки – способ 2 sum [] = 0 sum (x:xs) = x + sum xs • Слева от = м.б. выражение с переменными – шаблон (pattern) • Шаблоны м.б. довольно сложным • Примеры: f [x, y, z] f [1, y, 2] f (x:y:xs) f (x:1:xs) Замечания: • sum (x:xs) – скобки нужны! • Еще раз, правило линейности: • f [x, y, x] -- ошибка!
Еще пример • product – произведение чисел списка (стандартная функция) product [] = 1 product (x:xs) = x * product xs • Снова факториал fact n = product [1..n] • Это стиль Haskell! • Fritz RuehThe Evolution of a Haskell Programmerhttp://www.willamette.edu/~fruehr/haskell/evolution.html
++ • Еще одна стандартная функция • Конкатенация - оператор ++ [1,2] ++ [3,4] [1,2,3,4] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) • Кстати: как приписывать в конец списка • xs : x • Нет!: приписывает только в начало! • xs ++x • Нет!++ работает только со списками! • xs ++ [x] • OK • Но O(n) – медленно!
Пример: map • Взять синус от всех элементов списка sinAll [] = [] sinAll (x:xs) = sin x : sinAll xs • Взять косинус от всех элементов списка cosAll [] = [] cosAll (x:xs) = cos x : cosAll xs • Возвести все элементы списка в квадрат • :( Надо что-то делать! • map map f [] = [] map f (x:xs) = f x : map f xs • Примеры вызова map sin [1,2,3] [sin 1, sin 2, sin 3] map sqr [1,2,3] [1,4,9] • Функция высшего порядка!
Лямбда-выражения • Почему в обычных языках (Паскаль, C) этим мало пользуются? • Еще задача: К каждому числу в списке приписать 7 справа. f x = 10 * x + 7 map f xs • Надо описывать вспомогательные функции – лень • Надо придумывать имена – тоже лень • Лямбда-выражение – функция без имени • \i -> 10*x + 7 map (\i -> 10*i+7) xs • Синтаксис: \ параметр1 параметр2 ->выражение • \ - то, что осталось от λ • Ограничения: • М.б. только одно правило (но case) • Естественно, не м.б. рекурсивно
trace, show import Debug.Trace … trace строка значение … • Возвращает значение • Печатает строку • Пример: height Empty = trace "!!!" (-1) • show • show 66 "66" • Не требует import, есть почти для всех обьектов • show [1,2,3] "[1,2,3]" • Пример trace + show: fact i = trace (show i) i *fact (i-1)
Функции высшего порядка в C#Как описыватьпараметры-функции • Как описывать параметры – функции • Как задавать значения для параметров-функций • Как описывать: Func<int, int> f • Func<тип1, тип2, … ,тип_результата> Func<int, double, bool> • Это сокращение для delegate • Специальные случаи: • Predicate<int> pred • функция с одним параметром и логическим значением • Action<int> act Action<int, double> act1 • Функция возвращает void
Как задавать значения для параметров-функций • Что можно передавать в функцию с параметром-функцией? • Имя статического метода • Expression lambdaпараметр => выражение или (параметр, параметр, …) => выражение i => i*i (i, j) => i + j • Можно указывать типы (но обычно не нужно) (int i, int j) => i + j • Statement lambdaпараметр => блок или (параметр1, параметр, …) =>блок n => { int p = 0; for(int i=1;i<=n;i++) p*=i; return p; } • Есть еще возможности (delegate, не статические методы…)
Пример static void PrintTable( Func<int, int> f, int n) { for (int i = 0; i <= n; i++) { Console.WriteLine("{0} {1}", i, f(i)); } } static int Cube(int i) { return i * i * i; } // Примеры вызова: PrintTable(Cube, 10); Func<int, int> sqr = i => i * i; PrintTable(sqr, 10); PrintTable(i => i*i, 10);
Refential transparency • Что такое ‘побочный эффект’? • Прозрачность по ссылкам (referential transparency) … f 5 + … f 5 … • Вызовы с одинаковыми параметрами всегда дают одинаковые значения • Можно заменить на один вызов (с помощью let, например) Не очень понятно: • А как же случайные числа?? • А как же ввод/вывод??