Upload
-
View
99
Download
0
Embed Size (px)
Citation preview
Введение в функциональное программирование на
примере языка ML
Андрей Голованов, ФогСофтIT-Десант, 18.04.2013
Read – Evaluate – Print - Loop В Emacs используется интерпретатор
NJ/SML Статическая и динамическая среда
выполнения Рестарт для зачистки данных в среде
Среда исполнения REPL
val x = e; Синтаксис:
◦ Ключевые слова val = ;
◦ Переменная x◦ Выражение e
Поддерживаются вложенные выражения
val z = (x + y) + (y + 2);
Объявление переменных и выражения
Синтаксис (как записывается) Правила проверки типов
◦ Определение типа или ошибка◦ Примеры типов: int bool unit
Правила вычисления выражения (применяются после успешной проверки типа)◦ Возвращается значение или exception или
бесконечный цикл Демо...
Правила разбора выражений
val a = 1val b = a (* b присваивается 1 *)val a = 2
Не важно что будет дальше со значением a
Переменной нельзя присвоить значение повторно, его можно только затенить
«Затенение» переменных
Синтаксис:◦ e1 + e2 где e1 и e2 это выражения
Проверка типа:◦ Если e1 и e2 имеют тип int,
то e1 + e2 тоже будет иметь тип int Вычисление выражения:
◦ Если e1 вычисляется в v1 и e2 вычисляется в v2,то e1 + e2 рассчитывается как сложение v1 и v2.
Операция сложения
Значение – это выражение, которое не требует вычислений◦ 8+9 - это не значение
Любое значение – это выражение Не все выражения являются значениями Каждое значение «вычисляется в само в
себя» без • Примеры:
◦ 34, 17, 42 имеет тип int◦ true, false имеет тип bool◦ () имеет тип unit
Значение
Синтаксис: (e1, e2, … en) Проверка типов: если e1 – имеет тип
t1, … en – tn, то выражение имеет тип t1 *…* tn
Вычисление: e1 – v1, … en – vn, то результат (v1, … , vn)
Создание кортежа
Синтаксис: #1 e, #2 e, … Проверка типов: если e имеет тип t1*…
*tn, то #1 e имеет тип t1, … Вычисление: возвращается
соответствующий элемент кортежа
Допустимы вложенные кортежиval x1 = ((1,2), (3, (4,4)))
Доступ к элементам кортежа
Построение списков◦ [] – пустой список◦ [v1, v2, …, vN] – список◦ e1::e2 – добавление элемента e1 в список e2◦ e1@e2 – слияние списков e1 и e2
Проверка типов◦ Список содержит элементы одного типа.
Пример int list, bool list, int list list Вспомогательные функции
◦ null e, hd e, tl e, length e …
Списки
В сравнении с методом из ООП языков (Java, C#)◦ Нет ключевого слова this◦ Нет return инструкции◦ Часто в ФЯ применяются рекурсивные вызовы,
например для обхода элементов коллекции
Функции особенности
Синтаксис: fun x0 (x1 : t1, … , xn : tn) = e
Вычисление: функция – это значение! Добавляется x0 в динамическую среду
выполнения Проверка типов: значение функции
определяется автоматически через выведение типов
Объявляется переменная x0: (t1*t2…*tn)->t
Объявление функции
Синтаксис: e0 (e1, …, en) Проверка типов:
– e0 имеет тип (t1 * … * tn) -> t – e1 - t1, …, en - tn e0 (e1, … , en) имеет тип t Вычисляется e0 для fun x0 (x1 : t1, … , xn :
tn) = e1. Вычисляются значения аргументов v1, …, vn2. Результат вычисления e. Динамическая среда
расширена значениями x1 : v1, …, xn : vn, а также есть x0 – для рекурсивных вызовов
Вызов функции
Синтаксис◦ Let b1 b2 … bn in e end
bi – это объявление переменной, может присваиваться выражение, объявляться функция
Проверка типов◦ Тип всего выражения определяется типом e
Область видимости (scope) Реиспользование кода (уменьшение багов,
простота разработки, но тяжело вносить изменения в реиспользуемый код)
Выражение let
Рекурсивно заданная функция в своем определении содержит себя
Рекурсия заменяет циклы в языке ML Хвостовая рекурсия – дешевый вызов
рекурсии (пример расчета факториала с простой и хвостовой рекурсией)◦ Применение переменной аккумулятора
Рекурсия
t option – тип для любого типа t Типы options
◦NONE имеет тип ‘a option
◦SOME e имеет тип t option, если e имеет тип t
Работа с options◦isSome тип ‘a option -> bool (false если NONE)
◦valOf тип ‘a option -> ‘a (исключение, если NONE)
Options
Не возможно выполнять операцию присвоения к переменным, частям кортежей, элементам списка
Aliasing – доступ к одному и тому же объекту, используя разные переменные.
Имея 2 функции sort_pair1 и sort_pair2, не возможно отличить возвращается ли копия кортежа или alias, в случае выполнения условия if
Неизменяемость переменных (no mutation)
fun sort_pair1 (pair : int * int) =if #1 pair < #2 pairthen pairelse (#2 pair, #1 pair)
fun sort_pair2 (pair : int * int) =if #1 pair < #2 pairthen (#1 pair, #2 pair)else (#2 pair, #1 pair)
Неизменяемость переменных (no mutation) 2
Каковы преимущества?◦ Нет возможности отличить используется ли alias
для объекта или его копия◦ Нет необходимости думать об использовании
alias. Фокусируемся на других вещах◦ Можно смело использовать alias, не опасаясь
возможности изменения передаваемого объекта Эта особенность применяется в
параллельных вычислениях Пример из Java. В Java поддерживается
изменение значения созданных переменных
Неизменяемость переменных (no mutation) 3
Контейнер с именованными полями Синтаксис: {f1=e1, … , fn=en}, доступ
к полю #f1 {record}, f1 – имя поля. Кортежи – это синтаксический сахар для
записей вида {1=e1, 2=e2, …, n=en}.Кортеж (e1, e2, … en), доступ #1 (tuple).
Records (записи)
Разобрали объявление переменных, функций
Примерdatatype mytype = TwoInts of int * int
| Str of string| Pizza
создали новый тип mytype. Тип имеет 3 конструктора TwoInts, Str и Pizza
Создаем конструкторы, которые производят значения нового типа
Объявление datatype
Для определения какой конструктор использован для данного экземпляра типа используются case выражения
Демо с арифметическими выражениями
Работа со значениями datatype
Pattern-matching (англ.) Используется при объявлении переменной
вместо указания имени переменнойval p = e
Используется при объявлении функцииfun f p = e
Каждая функция принимает только один аргумент
Пример с суммированием элементов кортежа
Сопоставление шаблонов
Можно указать в конструкторе datatype использование любого типа
демо с option и бинарным деревом, суммирование узлов и листьев
Полиморфизм datatype
Объявление: exception <name> of t Вызов: raise <name> <значение> Обработка: e1 handle <name>
<параметры> => e2
Исключения
Функции первого класса – могут использоваться как значения.
Функции высшего порядка - могут принимать другие функции в качестве параметра.
Пример f(f(f...f(x))) n раз (n_times) Разбор типа: fn : ('a -> 'a) * int * 'a -> 'a
Функции первого классаФункции высшего порядка
У анонимной функции нет имени, поэтому её нельзя вызывать рекурсивно
fn args => e Пример, double_n_times
Анонимные функции
Область видимости (англ. scope) – это контекст выполнения программы, где по имени можно обращаться к переменным или функциям.
Область видимости в блоке, в функции, в let-выражении, глобальный, лексический
Lexical scope. Не важно где вызывают функцию, важно где её объявили
Область видимости функции
Значение функции состоит из двух частей:◦ Код функции◦ Среда, которая была на момент определения
функции Эта пара значений называет замыканием
функции Доступ к этим свойствам значения функции
невозможен в коде При вызове функции вычисляется код в
рамках среды (которая дополняется аргументами функции)
Пример работы lexical scope.
Closure (замыкание)
Тело функции не вычисляется пока функция не будет вызвана
Тело функции вычисляется каждый раз, когда функция вызывается
Выражение в определении переменной вызывается один раз и вычисляется, когда переменная объявляется.
Вычисление выражений и функций
Основные операции со списками filter, map, fold: f(f(f(acc, x1), x2), x3)
Демо с реализацией и использованием операций.
Основные операции со списками
Идиома – это элементарная конструкция типичная для одного или нескольких языков программирования в нашем случае для функциональных языков
Комбинирование функций (композиция) Currying Callback – подпись на события и
обратный вызов функций.
Идиомы в языке ML
SML использует жадную стратегию для вычислений
fun f(x:int):bool = true;fun arg () : int = 3;f(arg()) (* вызовет метод arg, хотя параметр x не используется *) Отложенные вычисления реализуются с
помощью обертки выражения анонимной функцией (thunk).
Жадное и ленивое вычисление
Статическая типизация – контроль за типами при компиляции до запуска программы (Pascal, C++, Java, C#)
Динамическая типизация – контроль типов во время выполнения программы (PHP, Python, Ruby, Perl, Javascript)
ML – статически типизированный ML – язык с неявным определением
типов. Применяется выведение типов
Статическая и динамическая типизация
Выведение типов – процесс определения типов выражений, переменных, который приведет к успешной проверке типов.
Задача выведения типов◦ Установить типы переменных в коде.◦ Удостовериться, что все выражения проходят
проверку типов.
Выведение типов
Неизменяемость значений переменных Использование функций в роли
аргумента Рекурсивные типы данных Использование рекурсии в функции для
обхода элементов списка Использование сопоставления шаблонов Ленивая инициализация
Особенности функциональных языков
Функции первого порядка, closures в C#, Java, Javascript
Анонимные функции в C#, Javascript LINQ в C# Callback в C#, Javascript Полиморфизм типов (generics) Неявная типизация, выведение типов в
C# и Javascript
Применение концепций ФЯ в ЯП
Типы и операции, матрица Разрешение задачи на ООЯП через
выделение типов и интерфейса Решение на ФЯП через определение
datatype и выделения операций с перечислением пар и pattern-matching’ом
Решение одной задачи на ФЯП и ООЯП
eval toString hasZero
Int
Add
Negate
Формулировка задачи
• В ФЯП декомпозиция по операциям• В ООП декомпозиция по
сущностям, выполняющим действия
задачи оптимизации, генетическое программировние, сложные доказательства эвристические задачи (например,
шахматы) современные языки программирования
включают в себя различные парадигмы программирования (multi-paradigm) в том числе многие идиомы из ФЯ (например, C#, Javascript).
Область применения ФЯ
Списки – это тоже datatypes Вложенные шаблоны, пример zip3,
nondecreasing, multsign sgn = P | N | Z Синонимы типов Модули и сигнатуры
Что осталось за кадром
Описание библиотечных типов языка SMLhttp://www.standardml.org/Basis/overview.html
Ссылки