Философия программирования на языке С++ (2-е издание) - 2006
Лекция о языке программирования Haskell
-
Upload
husniyarova -
Category
Technology
-
view
229 -
download
3
description
Transcript of Лекция о языке программирования Haskell
Cool Haskell In Action
Влад и ЖеняСпециально для DMLabs
Санкт-Петербург17 ноября 2013
О чем пойдет речь● Краеугольные камни ФП● Быстрый интро● Основные фишки и приемы в Haskell● Сравнение с другими языками ФП + и -● Красивые примеры, элегантный код● Функциональный подход к проектированию систем● Зачем стоит изучать Haskell
Краеугольные Камни ФП● Функции Высших Порядков. Мы в мире функций!● Рекурсия. Нет понятия цикл● Чистые функции (детерминированность, отсутствие
побочных эффектов)● Модель вычислений без состояний (нет
присваивания => нет переменных)
Сильные и слабые стороны ФП● Нет необходимости отслеживать побочные эффекты
=> надежность кода● Нет нужды проверять внешнее состояние программы
=> простота модульного тестирования ● Возможность автоматического распараллеливания● Оптимизация при компиляции
● Слабое быстродействие (за счет порождения новых данных). Нужен эффективный сборщик мусора
● Нелепица при вводе-выводе
Основы Синтаксиса● Типы данных - Integer, Int, Bool, Char, Float, Double● Сравнения - < <= > >= == /=● Логические операции - && || not● Функции - нет ни запятых, ни скобок
Пример:1) sqr x = x*x● Конструкция if then else
Пример:abs x = if x > 0 then x else –x
● Рекурсия основной строительный блокfact 1 = 1fact n = fact (n-1) * n
Накапливающийся параметрfact n = fact’ n 1fact' 1 p = pfact' n p = 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
5 14 53 202 601 120
Списки● [] - пустой список● [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]
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)
Задачки● gen_list 5 => [5,4,3,2,1]● mylength [1,2,3,4] => 4● rev [1,2,3,4] => [4,3,2,1]● rev используя накапливающиеся параметры
Решения1) gen_list 0 = [] gen_list n = n : f (n-1)2) mylength [] = 0 mylength xs = 1 + length (tail xs) 3) rev1 [] = [] rev1 xs = rev2 (tail xs) ++ [x]
rev2 [] = [] rev3 xs = last xs : rev(init xs)
Решения4) rev xs = rev’ xs [] rev’ [] ys = ys rev‘ xs ys = rev’ (tail xs) ((head x) : ys)
Pattern Matching 2sum [] = 0sum (x:xs) = x + sum xs
Могут быть сложными:min_in_list [] = 1/0min_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)
Лямбда-выражения и Каррирование
● По простому лямбда-выражения - это функции без имени● В Haskell \x -> x*x● Каррирование - сопоставление функции от многих переменных
функцию берущую свои аргументы по одномуПример:mysum x y z = x + y + z ⇔ mysum2 = \x -> \y -> \z -> x + y + zВ чем плюс?mysum42 = mysum2 42
Основные функции высших порядков
● 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 xsproduct xs = foldr (*) 1 xs
Понимание foldrfoldr 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
Задачки● myzip● mymap● myfoldr● операция and для списка через foldr● prod_mod3 [1,2,3,4,5,6] => 3*6 = 18
Решения1) myzip [] _ = [] myzip _ [] = [] myzip (x:xs) (y:ys) = (x,y) : myzip xs ys
Note: map (\(x,y) -> x+y) [(1,2), (3,4), (5,6)] => [3, 7, 11]
2) mymap func [] = [] mymap func (x:xs) = func x : mymap func xs
Решения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
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 TreeNode 1 (Node 2 Empty Empty) (Node 3 Empty Empty)
sumTree Empty = 0sumTree (Node val l r) = val + sumTree l + sumTree r
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] ]
Сравнение Haskell с другими ФПВсе языки ФП имеют теже преимущества, что и Haskell, НО:● Для преодоления нелепицы при вводе-выводе остальные ФП
используют хаки и становятся не чисто функциональными● Haskell же использует монады и остается чистым, а значит сохраняет
все преимущества (надежность, распараллеливаемость, простоту тестирования) в любых ситуациях
про монады и в следующий раз :)
Pattern matchingЗадаются строчки z и y.
Можно ли получить строку z из строки y путем выбрасывания символов?
(строка = массив символов)
Pattern matchingМожно ли получить строку z из строки y путем выбрасывания символов?
func [] [] = True
func [] (ys) = True
func (zs) [] = False
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
Крутокод на Сиint func(char* y, char* z) { while (*y) { if (!(*z)) { return false; } if (*y==*z) { y++; }; z++; } return true;}
Pattern Matchingquicksort [] = []
quicksort (p:xs) =
(quicksort lesser) ++ [p] ++ (quicksort greater)
where
lesser = filter (< p) xs
greater = filter (>= p) xs
Cool quicksortqsort [] = []
qsort (x:xs) = qsort [t | x<-xs, t<x]
++ [x]
++ qsort [t | x<-xs, t>x]
Интерпретатор скриптового языка
Написать интерпретатор, который принимает список операций над стеком, и возвращает содержимое стека после выполнения операций:PUSH 1
PUSH 5
POP
= > [1]
Интерпретатор скриптового языкаfunc "PUSH" (a:[]) (xs) = (a:xs)
func "POP" ([]) (x:xs) = (xs)
func _ _ x = x
Интерпретатор скриптового языкаfunc "PUSH" (a:[]) (xs) = (a:xs)
func "POP" ([]) (x:xs) = (xs)
func _ _ x = x
data CommandInfo = Command String [Int]
process [] stack = stack
process (x:xs) stack = process xs (apply x)
where
apply (Command cname cargs) = func cname cargs stack
execute xs = process xs []
Интерпретатор скриптового языкаexecute [
Command "PUSH" [1],
Command "PUSH" [2],
Command "PUSH" [3],
Command "POP" []
]
[2,1]
Интерпретатор скриптового языкаДописать команду ADD, которая будет прибавлять число-аргумент к вершине стека.
func "PUSH" (a:[]) (xs) = (a:xs)
func "POP" ([]) (x:xs) = (xs)
func _ _ x = x
Интерпретатор скриптового языкаfunc "PUSH" (a:[]) (xs) = (a:xs)
func "POP" ([]) (x:xs) = (xs)
func "ADD" (a:[]) (x:xs) = ((a+x):xs)
func _ _ x = x
Интерпретатор скриптового языка
Написать интерпретатор, который принимает список операций над стеком.
PUSH -> добавляет в стек; ничего не выводит
SUM -> выводит сумму
POP -> выводит удаленный элемент
CLS -> Очищает вывод
Интерпретатор скриптового языкаPUSH 1 [1] []
PUSH 2 [1, 2] []
PUSH 5 [1, 2, 5] []
PUSH 3 [1, 2, 5, 3] []
POP [1, 2, 5] [3]
CLS [1, 2, 5] []
POP [1, 2] [5]
SUM [1, 2] [5, 3]
Интерпретатор скриптового языка
Интерпретатор №1:
Стек -> (функция) -> Стек’
Интерпретатор №2:
(Стек+Вывод) -> (функция) -> (Стек+Вывод)’
Состояние вычисления = Стек+Выводdata Result = State [Int] [Int]
Интерпретатор скриптового языка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
Интерпретатор скриптового языка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 [] [])
Язык \ подход
Ф-подход:данные -> (функция) -> данные’
ОО-подход:объект = данные + функции
Сравнение подходовЗадает ли язык программирования ограничение на парадигму?
Сравнение подходовclass Vector2 { public: float x, y;
void Add(Vector2 &other) {x += other.x;y += other.y;
}
Vector2 Add2(Vector2 &other) {Vector2 result;result.x = x + other.x;result.y = y + other.y;return result;
}
VS
Применимость подходовФ-подход:
Программа ориентирована на обработку (алгоритмы шифрования, эвалюаторы)
Легко изменяется функционал
ОО-подход:Программа ориентирована на данные (прикладные программы)Легко изменяются данные/источники
Дополнительная задача №1Есть фигуры: эллипс, квадрат, прямоугольник1. Построить иерархию классов2. Написать функцию, которая увеличивает ширину фигуры в x раз(площадь тоже увеличивиается в x раз)
ShapeEllipse Rectangle
Square
Дополнительная задача №1void enlarge(float coef)
vs
Shape enlarge(float coef)
Дополнительная задача №2void myownhugedataset::operation() {
try {
a = b;
c *= 2;
...
} catch(exception e) {
printf(":(");
//Как восстановить рабочее состояние объекта?
}
}
Дополнительная задача №2myownhugedataset myownhugedataset::operation() {
myownhugedataset copy = *this;
try {
copy = func1(copy);
...
} catch(exception e) {
return *this;
}
return copy;
}
Зачем стоит изучать Haskell