1. Cool Haskell In Action
Влад и Женя
Специально для DMLabs
Санкт-Петербург
17 ноября 2013
2. О чем пойдет речь
●
●
●
●
●
●
●
Краеугольные камни ФП
Быстрый интро
Основные фишки и приемы в Haskell
Сравнение с другими языками ФП + и Красивые примеры, элегантный код
Функциональный подход к проектированию систем
Зачем стоит изучать Haskell
3. Краеугольные Камни ФП
● Функции Высших Порядков. Мы в мире функций!
● Рекурсия. Нет понятия цикл
● Чистые функции (детерминированность, отсутствие
побочных эффектов)
● Модель вычислений без состояний (нет
присваивания => нет переменных)
4. Сильные и слабые стороны ФП
● Нет необходимости отслеживать побочные эффекты
=> надежность кода
● Нет нужды проверять внешнее состояние программы
=> простота модульного тестирования
● Возможность автоматического распараллеливания
● Оптимизация при компиляции
● Слабое быстродействие (за счет порождения новых
данных). Нужен эффективный сборщик мусора
● Нелепица при вводе-выводе
5. Основы Синтаксиса
●
●
●
●
Типы данных - Integer, Int, Bool, Char, Float, Double
Сравнения - < <= > >= == /=
Логические операции - && || not
Функции - нет ни запятых, ни скобок
Пример:
1) sqr x = x*x
●
Конструкция if then else
●
Рекурсия основной строительный блок
fact 1 = 1
fact n = fact (n-1) * n
Пример:
abs x = if x > 0
then x
else –x
7. Списки
●
●
●
●
[] - пустой список
[1,2,3,4] или [1..n] - элементы должны быть одного типа
Оператор : - приписать элемент в начало. 1: [2,3] => [1,2,3]
++ - конкатенация списков. [1,2] ++ [3,4] => [1,2,3,4]
Функции для работы со списками:
●
●
●
●
●
●
head [1,2,3,4] => 1
tail [1,2,3,4] => [2,3,4]
length [1,2,3,4] => 4
zip [1,2] [3,4] => [(1,3), (1,4), (2,3), (3,4)] + fst и snd
last [1,2,3,4] => 4 - очень медленно, не рекомендуется
init [1,2,3,4] => [1,2,3]
8. Pattern Matching 1
●
Идея в том, чтобы описывать преобразования над данными, а не
точную последовательность действий
Примеры:
1) fact n = fact’ n 1
fact' 1 p = p
fact' n p = fact’ (n-1) (n*p)
2) sum [] = 0
sum xs = head xs + sum(tail xs)
12. Pattern Matching 2
sum [] = 0
sum (x:xs) = x + sum xs
Могут быть сложными:
min_in_list [] = 1/0
min_in_list [x] = x
min_in_list (x:y:xs) = if x < y then min_in_list(x:xs) else min_in_list(y:xs)
13. Лямбда-выражения и
Каррирование
●
●
●
По простому лямбда-выражения - это функции без имени
В Haskell x -> x*x
Каррирование - сопоставление функции от многих переменных
функцию берущую свои аргументы по одному
Пример:
mysum x y z = x + y + z ⇔ mysum2 = x -> y -> z -> x + y + z
В чем плюс?
mysum42 = mysum2 42
14. Основные функции высших
порядков
●
●
●
map func xs - map (x -> 10*x +5) [1,2,3,4] => [15, 25, 35, 45]
filter cond xs - filter (x -> x > 0) [-1,1,-3,4] => [1,4]
foldr и foldl - свертка
Пример:
sum xs = foldr (+) 0 xs
product xs = foldr (*) 1 xs
15. Понимание foldr
foldr f e xs
Как бы:
res = e
для каждого x - элемента цикла (справа налево) {
res = f x res
}
Пример:
foldr (x res -> if x>0 then x+res else res) 0 [1,-3,-7,4,9] => 14
18. Решения
3) myfoldr f e [] = e
myfoldr f e (x:xs) = f e (myfoldr f e xs)
myfoldr f e (x:xs) = e `f` (myfoldr f e xs)
4) and xs = foldr (&&) True xs
5) prod_mod3 xs = foldr (x y -> if mod x 3 == 0 then x*y else y) 1 xs
19. Data с вариантами
data Point = Pt Integer Integer
●
Для доступа к полям - pattern matching
dist (Pt x y) = sqrt (x*x + y*y)
data Person = Student String Integer Integer | Professor String String
[Student “Иванов” 2 541, Professor “Моль” “Геометрия”]
data Tree = Empty | Node Integer Tree Tree
Node 1 (Node 2 Empty Empty) (Node 3 Empty Empty)
sumTree Empty = 0
sumTree (Node val l r) = val + sumTree l + sumTree r
20. List Comprehension
Хотим делать что-то вроде S = { x: x <- R, x > 5 }
Пожалуйста - [sin x | x <- [1..n]] => [sin 1, sin 2, … , sin n]
Более сложно:
identity n = [ [ if i == j then 1 else 0 i <- [1..n] | j <- [1..n] ]
21. Сравнение Haskell с другими ФП
Все языки ФП имеют теже преимущества, что и Haskell, НО:
●
●
Для преодоления нелепицы при вводе-выводе остальные ФП
используют хаки и становятся не чисто функциональными
Haskell же использует монады и остается чистым, а значит сохраняет
все преимущества (надежность, распараллеливаемость, простоту
тестирования) в любых ситуациях
про монады и в следующий раз :)
22. Pattern matching
Задаются строчки z и y.
Можно ли получить строку z из строки y путем
выбрасывания символов?
(строка = массив символов)
23. Pattern matching
Можно ли получить строку z из строки y путем
выбрасывания символов?
func [] [] = True
func [] (ys) = True
func (zs) [] = False
24. Pattern matching
Можно ли получить строку z из строки y путем
выбрасывания символов?
func [] [] = True
func [] (ys) = True
func (zs) [] = False
func (z:zs) (y:ys)
| z==y
= func zs ys
| otherwise
= func (z:zs) ys
25. Крутокод на Си
int func(char* y, char* z) {
while (*y) {
if (!(*z)) {
return false;
}
if (*y==*z) {
y++;
};
z++;
}
return true;
}
28. Интерпретатор скриптового языка
Написать интерпретатор, который принимает список
операций над стеком, и возвращает содержимое стека
после выполнения операций:
PUSH 1
PUSH 5
POP
= > [1]
32. Интерпретатор скриптового языка
Дописать команду ADD, которая будет прибавлять
число-аргумент к вершине стека.
func "PUSH" (a:[]) (xs) = (a:xs)
func "POP" ([]) (x:xs) = (xs)
func _ _ x = x
33. Интерпретатор скриптового языка
func "PUSH" (a:[]) (xs) = (a:xs)
func "POP" ([]) (x:xs) = (xs)
func "ADD" (a:[]) (x:xs) = ((a+x):xs)
func _ _ x = x
34. Интерпретатор скриптового языка
Написать интерпретатор, который принимает список
операций над стеком.
PUSH -> добавляет в стек; ничего не выводит
SUM -> выводит сумму
POP -> выводит удаленный элемент
CLS -> Очищает вывод
36. Интерпретатор скриптового языка
Интерпретатор №1:
Стек -> (функция) -> Стек’
Интерпретатор №2:
(Стек+Вывод) -> (функция) -> (Стек+Вывод)’
Состояние вычисления = Стек+Вывод
data Result = State [Int] [Int]
37. Интерпретатор скриптового языка
data Result = State [Int] [Int]
func "PUSH" (a:[]) (State stack out) = State (a:stack) out
func "POP" [] (State (x:stack) out) = State stack (out++
[x])
func "SUM" [] (State stack out) = State stack (out++[sum
stack])
func "CLS" [] (State stack out) = State stack []
func _ _ x = x
38. Интерпретатор скриптового языка
data Result = State [Int] [Int]
...
data CommandInfo = Command String [Int]
process [] (State stack out) = out
process (x:xs) state = process xs (apply x)
where
apply (Command cname cargs) = func cname cargs state
execute xs = process xs (State [] [])
41. Сравнение подходов
class Vector2 { public: float x, y;
void Add(Vector2 &other) {
x += other.x;
y += other.y;
}
VS
Vector2 Add2(Vector2 &other) {
Vector2 result;
result.x = x + other.x;
result.y = y + other.y;
return result;
}
42. Применимость подходов
Ф-подход:
Программа ориентирована на обработку (алгоритмы шифрования,
эвалюаторы)
Легко изменяется функционал
ОО-подход:
Программа ориентирована на данные (прикладные программы)
Легко изменяются данные/источники
43. Дополнительная задача №1
Есть фигуры: эллипс, квадрат, прямоугольник
1. Построить иерархию классов
2. Написать функцию, которая увеличивает ширину фигуры в x раз
(площадь тоже увеличивиается в x раз)
Shape
Ellipse
Rectangle
Square