
FP I (Intro, Turing Machine).pptx
- Количество слайдов: 51
Функциональное программирование I Парадигмы программирования. Мотивация. Модели вычислений.
Обзор курса • Парадигмы программирования и языки • Модели вычислений. Лямбдаисчисление, типы и F# • Примеры: символьные вычисления, language-oriented programming, DSL, backtracking, проектирование, эмуляция физики в играх.
Prerequisites Программирование: • Знакомство с императивным программированием: переменные, циклы, массивы, структуры, … • Базовое знакомство с ООП: классы и объекты, поля, методы, конструкторы, свойства, инкапсуляция, наследование, полиморфизм (подтиповой и ad hoc)
• Базовые структуры и алгоритмы обработки данных • Начальные знания языка программирования C# и платформы. Net Математика: • Классическая логика и логика первого порядка, наивная теория множеств • Начальные знания комбинаторики и дискретной теории вероятности • Индукция
Рекомендуемая литература “Introduction to functional programming” — John Harrison “A short introduction to the Lambda Calculus”— Achim Jung “Types and Programming Languages”— Pierce “Lambda-Calculus and Combinators, an Introduction”— J. R. Hindley, J. P. Seldin “An Introduction to lambda-calculus and arithmetic with a decent selection of exercises”— H. Simmons, A. Schalk
“Functional Programming for the Real World” — Tomas Petricek, Jon Skeet “Programming F#”— Chris Smith “Expert F# 2. 0” — Don Syme “F# for Scientists”— Jon Harrop “Algorithms: A Functional Programming Approach” — Rabhi, Lapalme “Functional Programming in C#” — Sturm Oliver Лекции Andrew Ng с ml-class. org “Real World Haskell” — Bryan O'Sullivan, Donald Bruce Stewart, John Goerzen
Почему программировать сложно? Программирование — это способ взаимодействия с компьютером. Компьютеры и люди различаются, им нужен некий общий язык. Такой общий язык изначально конструировался как лёгкий для восприятия компьютером, лёгкий для перевода в машинные команды. Возможно, если язык будет ближе человеку, программировать будет проще?
Эволюция языков ASM Fortran, LISP Pascal, C, Scheme, . . . C++, Perl, Java, C#, Python, . . .
Старт Финиш
Парадигмы программирования •
Императивное программирование •
Задача: Посчитать сумму целых чисел от 1 до 10000. private static int Sum() { var sum = 0; for (var number = 1; number < 10000; number++) { sum = sum + number; } return sum; } Вопрос: Сколько раз будет изменено состояние программы в этом примере?
Ответ зависит от нашего понимания состояния. Если мы будем рассматривать состояние как значения всех локальных переменных (не учитывая текущее значение instruction pointer’a и т. д. ), то мы можем сказать, что состояние было изменено 20000 раз.
Состояние как источник сложности Изменения состояний сложно анализировать, следовательно, сложно доказать корректность программы. Состояния трудно отслеживать: в любой нетривиальной программе происходят миллиарды изменений состояний — мы практически никогда не можем сказать, что на самом деле находится в некой переменной в конкретный момент времени.
•
•
Функциональное программирование — это парадигма программирования, представляющая программу как вычисление математической функции без использования изменяемых данных и состояний. ФП трактует функции как объекты первого класса — содержит конструкции, позволяющие работать с функциями также легко, как с любыми другими данными например, целыми числами.
Например, наш пример мы можем написать на функциональном языке следующим образом: let sum = List. sum [1. . 10000] sum — это не переменная, а поименованное значение. Обратите внимание: нам больше не нужна локальная переменная n, как и цикл.
Функциональное программирование избегает т. н. сайд-эффектов. Side effect — это свойство выражения/функции. Кроме возврата некого результирующего значения, функции с сайд-эффектами меняют состояние, например: Ø изменяют значение глобальных переменных Ø изменяют значение переданных им аргументов Ø совершают ввод/вывод Ø вызывают другие выражения/функции с сайдэффектами.
Отсутствие/Выделение побочных эффектов: Ø Повышение надёжности программ Ø Облегчение параллельного программирования Ø Облегчение анализа программ и доказательства корректности. ФП является одним из вариантов т. н. декларативного программирования: мы описываем не то, как получить решение, а что такое решение.
Уравнение Беллмана let a. OS = [ for outcomes in (possible. Outcomes state) -> List. fold (fun acc (state', probability) -> acc + probability * U. [state']) 0. outcomes ] if is. Terminal state |> not then U'. [state] <- reward state + gamma * (List. max a. OS)
Быстрая сортировка На Haskell: quicksort [] = [] quicksort (p: xs) = (quicksort lesser) ++ [p] ++ (quicksort greater) where lesser = filter (< p) xs greater = filter (>= p) xs На F#: let rec quicksort = function | [] -> [] | x: : xs -> let lesser = List. filter ((>) x) xs let greater = List. filter ((<=) x) xs (quicksort lesser) @ [x] @ (quicksort greater)
На C: void qsort(int a[], int lo, int hi) { int h, l, p, t; if (lo < hi) { l = lo; h = hi; p = a[hi]; do { while ((l < h) && (a[l] <= p)) l = l+1; while ((h > l) && (a[h] >= p)) h = h-1; if (l < h) { t = a[l]; a[l] = a[h]; a[h] = t; } } while (l < h); a[hi] = a[l]; a[l] = p; qsort( a, lo, l-1 ); qsort( a, l+1, hi ); } } Решения на Haskell и F# работают с любыми типами данных, для которых определено сравнение: автогенерализация. Решение на C работает только со списками целых чисел.
Вставка в AVL-дерево На C++: int insert(tree_node_t *tree , key_tne w_key, object_t *new_object) { tree_node_t*t mp_node; int finished; if( tree->left == NULL ) { tree->left = (tree_node_t*)new_object; tree->key = ne w_key; tree->height= 0; tree->right = NULL; } else { create_stack(); tmp_node = tree; На F#: let rec add. Vertex. AVL v = function | AVLEmpty -> AVLNode(v, 1, AVLEmpty, AVLEmpty) | AVLNode(v', _, left, right) when v < v' -> let new. Left = add. Vertex. AVL v left let left. Root = get. Root new. Left let h. Root = (max (height new. Left) (height right)) + 1 while( tmp_node->right != NULL ) if (height new. Left - height right) = 2 then { push(tmp_node ); . . . 116 строк 24 строки
Hole in the Middle //инициализация var bmp = new Bitmap(width, height) using(var gr = Graphics. From. Image(bmp)) { (. . . ) //основные действия } Функции высшего порядка: var bmp = Draw. Image(width, height, gr => { (. . . ) //основные действия });
Undo ООП — иерархия классов:
Выполнение команды:
Отмена команды:
ФП (F#): type Op. Type = (Data. Type -> Data. Type) * Data. Type let performed. Ops = Stack
Данный подход имеет некоторые дополнительные преимущества перед использованием ООП: • Можно легко создавать макросы и сложные операции с помощью композиции или pipelining’a простых операций • Можно создавать сложные структуры данных, содержащие операции, например для динамического построения меню.
Несколько фактов Долгие годы во многих университетах США и Европы функциональные языки преподаются как первый язык программирования. Emacs написан большей частью на LISP используется в Auto. CAD. Ericsson разработала функциональный язык Erlang – разработка приложений, использующих параллельные вычисления, ускорилась в 5 -25 раз, длина программ сократилась в 2 -5 раз. Erlang так же используют T-Mobile, Nortel и Facebook.
2000—настоящее время: Бум ФП Большая часть серверного кода Twitter была переписана с языка Ruby (ОО/динамический) на язык Scala (функциональный/ОО, статическая типизация) – таким образом разработчики решили проблемы, возникающие из-за активного роста числа пользователей сервиса. Microsoft разработала и включила в состав Visual Studio 2010 язык F# - диалект OCaml’a, функциональный/OO/модульный. Microsoft планирует использовать его для масштабируемых , асинхронных и реактивных приложений, использующих параллельные вычисления. Также F# можно использовать как скриптовый язык. Часть движка Bing была переписана на F#.
Языки, сочетающие ОО и ФП Ø Ø Ø Ø Erlang (1986) Perl (1987)* Python (1991) Ruby (1995) OCaml (1996) C# (2001) F# (2002) Groovy (2003) Boo (2003) Scala (2003) Nemerle (2006) ОО диалекты Haskell’a: O’Haskell, Haskell++, Mondrian В Common Lisp было включено объектное расширение CLOS, так что теперь он тоже сочетает обе парадигмы *Курсив – слабая поддержка ФП
FP’s Pros & Cons Pros Cons Скорость разработки Скорость и используемая память Облегчение поддержки Сложность начального обучения Надёжность и корректность Статическая типизация с выведением типов Краткость синтаксиса и кода Работа со сложными структурами данных Параллельные, реактивные и асинхронные приложения Интерпретатор + компилятор Чёткий математический базис Модульность LOP/DSLs
Map. Reduce “Our abstraction is inspired by the map and reduce primitives present in Lisp and many other functional languages. . Our use of a functional model with user-specified map and reduce operations allows us to parallelize large computations easily and to use reexecution as the primary mechanism for fault tolerance. ” Jeffrey Dean and Sanjay Ghemawat, “Map. Reduce: Simplified Data Processing on Large Clusters” Map 1 Input Reduce 1 Map 2 Reduce 2 . . . Map n . . . Reduce m Output
There’s no silver bullet ФП — далеко не лучший вариант, если вы хотите написать: Ø GUI Ø ПО, критичное по скорости, но не масштабируемое (e. g. : система компьютерной алгебры для пользовательских машин)
Модели вычислений — это описание абстракции и набора допустимых операций для представления вычисления. Например: машина Тьюринга, машина Поста, лямбда-исчисление, рекурсивные функции, … Модели вычислений используются в анализе алгоритмов и служат отправной точкой в дизайне языков программирования.
Машина Тьюринга — это абстрактное устройство с памятью, читающее и записывающее символы на бесконечную ленту. Действия устройства зависят от символа на ленте и состояния устройства. Алгоритм задаётся конечной таблицей, представляющей функцию перехода.
Пример: сложение чисел Задача: Необходимо сложить 2 числа, записанные на ленте. Ввод: Числа представлены в виде последовательности единиц. Головка находится на первой единице первого числа. Числа разделены одной ячейкой со знаком «+» . Вывод: Головка находится на ячейке ленты, следующей за последней цифрой суммы.
\ \ 1 1 1 \ \ S = s 0 L = “ 1” A = R Идём до пустой ячейки S = s 0 L = “” S = s 1 L = “ 1” A = R Ставим вместо неё единицу S = s 1 L = “ 1” A = R Идём до конца 2 ого числа S = s 1 L = “\” S = s 2 L = “\” A = L Возвращаемся к последней цифре 2 ого числа S = s 2 L = “ 1” S = FIN L = “\” A = None Стираем последнюю цифру \ \ 1 1 1 \ \ \ S – state, состояние устройства L – letter, символ на ленте A – action, действие, которое необходимо выполнить
Формальное определение •
•
• s 0, `1`, R s 1, `1`, R s 1, `\` s 2, `\`, L s 2, `1`
Задание #1 Напишите спецификацию машины Тьюринга, убирающей все пробелы из строки, кончающейся восклицательным знаком. Например, для: H E L L O , W O R L D ! \ она вернёт: H E L W
Машина Тьюринга и императивное программирование Машина Тьюринга работает за счёт изменения состояния. Таким образом, это, по сути, модель императивного программирования. Стоит, однако, отметить, что машина Тьюринга мощнее реальных компьютеров — у неё есть бесконечная память.
Недетерминированная машина Тьюринга •
Результат мы получим, когда одна из этих машин завершится. Любую НМТ можно выразить как ДМТ с тремя лентами, первая из которых всегда содержит начальную входную строку, вторая используется для эмуляции одного из текущих вычислений НМТ, а третья — для представления пути в дереве вычислений, приведшего к вычислению на второй. Такую трёхленточную ДМТ можно легко свести к обычной одноленточной.
Таким образом, ДМТ может посчитать всё то же, что и НМТ, но медленнее. Это сводится к самому главному нерешённому вопросу в теоретической информатике: равны ли классы сложности P и NP? Существует также универсальная машина Тьюринга, могущая эмулировать любую машину Тьюринга. Её можно построить, что и было сделано Тьюрингом. Таким образом, впервые была получена модель компьютера с хранимой программой.