Лекция о языке программирования Haskell

47
Cool Haskell In Action Влад и Женя Специально для DMLabs Санкт-Петербург 17 ноября 2013

description

Лекция от студентов образовательной программы Data Mining Track компании Data Mining Labs

Transcript of Лекция о языке программирования Haskell

Page 1: Лекция о языке программирования Haskell

Cool Haskell In Action

Влад и ЖеняСпециально для DMLabs

Санкт-Петербург17 ноября 2013

Page 2: Лекция о языке программирования Haskell

О чем пойдет речь● Краеугольные камни ФП● Быстрый интро● Основные фишки и приемы в Haskell● Сравнение с другими языками ФП + и -● Красивые примеры, элегантный код● Функциональный подход к проектированию систем● Зачем стоит изучать Haskell

Page 3: Лекция о языке программирования Haskell

Краеугольные Камни ФП● Функции Высших Порядков. Мы в мире функций!● Рекурсия. Нет понятия цикл● Чистые функции (детерминированность, отсутствие

побочных эффектов)● Модель вычислений без состояний (нет

присваивания => нет переменных)

Page 4: Лекция о языке программирования Haskell

Сильные и слабые стороны ФП● Нет необходимости отслеживать побочные эффекты

=> надежность кода● Нет нужды проверять внешнее состояние программы

=> простота модульного тестирования ● Возможность автоматического распараллеливания● Оптимизация при компиляции

● Слабое быстродействие (за счет порождения новых данных). Нужен эффективный сборщик мусора

● Нелепица при вводе-выводе

Page 5: Лекция о языке программирования 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

Page 6: Лекция о языке программирования Haskell

Накапливающийся параметр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

Page 7: Лекция о языке программирования Haskell

Списки● [] - пустой список● [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]

Page 8: Лекция о языке программирования Haskell

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)

Page 9: Лекция о языке программирования Haskell

Задачки● gen_list 5 => [5,4,3,2,1]● mylength [1,2,3,4] => 4● rev [1,2,3,4] => [4,3,2,1]● rev используя накапливающиеся параметры

Page 10: Лекция о языке программирования Haskell

Решения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)

Page 11: Лекция о языке программирования Haskell

Решения4) rev xs = rev’ xs [] rev’ [] ys = ys rev‘ xs ys = rev’ (tail xs) ((head x) : ys)

Page 12: Лекция о языке программирования Haskell

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)

Page 13: Лекция о языке программирования Haskell

Лямбда-выражения и Каррирование

● По простому лямбда-выражения - это функции без имени● В Haskell \x -> x*x● Каррирование - сопоставление функции от многих переменных

функцию берущую свои аргументы по одномуПример:mysum x y z = x + y + z ⇔ mysum2 = \x -> \y -> \z -> x + y + zВ чем плюс?mysum42 = mysum2 42

Page 14: Лекция о языке программирования Haskell

Основные функции высших порядков

● 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

Page 15: Лекция о языке программирования Haskell

Понимание 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

Page 16: Лекция о языке программирования Haskell

Задачки● myzip● mymap● myfoldr● операция and для списка через foldr● prod_mod3 [1,2,3,4,5,6] => 3*6 = 18

Page 17: Лекция о языке программирования Haskell

Решения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

Page 18: Лекция о языке программирования Haskell

Решения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

Page 19: Лекция о языке программирования Haskell

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

Page 20: Лекция о языке программирования Haskell

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] ]

Page 21: Лекция о языке программирования Haskell

Сравнение Haskell с другими ФПВсе языки ФП имеют теже преимущества, что и Haskell, НО:● Для преодоления нелепицы при вводе-выводе остальные ФП

используют хаки и становятся не чисто функциональными● Haskell же использует монады и остается чистым, а значит сохраняет

все преимущества (надежность, распараллеливаемость, простоту тестирования) в любых ситуациях

про монады и в следующий раз :)

Page 22: Лекция о языке программирования Haskell

Pattern matchingЗадаются строчки z и y.

Можно ли получить строку z из строки y путем выбрасывания символов?

(строка = массив символов)

Page 23: Лекция о языке программирования Haskell

Pattern matchingМожно ли получить строку z из строки y путем выбрасывания символов?

func [] [] = True

func [] (ys) = True

func (zs) [] = False

Page 24: Лекция о языке программирования Haskell

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

Page 25: Лекция о языке программирования Haskell

Крутокод на Сиint func(char* y, char* z) { while (*y) { if (!(*z)) { return false; } if (*y==*z) { y++; }; z++; } return true;}

Page 26: Лекция о языке программирования Haskell

Pattern Matchingquicksort [] = []

quicksort (p:xs) =

(quicksort lesser) ++ [p] ++ (quicksort greater)

where

lesser = filter (< p) xs

greater = filter (>= p) xs

Page 27: Лекция о языке программирования Haskell

Cool quicksortqsort [] = []

qsort (x:xs) = qsort [t | x<-xs, t<x]

++ [x]

++ qsort [t | x<-xs, t>x]

Page 28: Лекция о языке программирования Haskell

Интерпретатор скриптового языка

Написать интерпретатор, который принимает список операций над стеком, и возвращает содержимое стека после выполнения операций:PUSH 1

PUSH 5

POP

= > [1]

Page 29: Лекция о языке программирования Haskell

Интерпретатор скриптового языкаfunc "PUSH" (a:[]) (xs) = (a:xs)

func "POP" ([]) (x:xs) = (xs)

func _ _ x = x

Page 30: Лекция о языке программирования Haskell

Интерпретатор скриптового языка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 []

Page 31: Лекция о языке программирования Haskell

Интерпретатор скриптового языкаexecute [

Command "PUSH" [1],

Command "PUSH" [2],

Command "PUSH" [3],

Command "POP" []

]

[2,1]

Page 32: Лекция о языке программирования Haskell

Интерпретатор скриптового языкаДописать команду ADD, которая будет прибавлять число-аргумент к вершине стека.

func "PUSH" (a:[]) (xs) = (a:xs)

func "POP" ([]) (x:xs) = (xs)

func _ _ x = x

Page 33: Лекция о языке программирования Haskell

Интерпретатор скриптового языкаfunc "PUSH" (a:[]) (xs) = (a:xs)

func "POP" ([]) (x:xs) = (xs)

func "ADD" (a:[]) (x:xs) = ((a+x):xs)

func _ _ x = x

Page 34: Лекция о языке программирования Haskell

Интерпретатор скриптового языка

Написать интерпретатор, который принимает список операций над стеком.

PUSH -> добавляет в стек; ничего не выводит

SUM -> выводит сумму

POP -> выводит удаленный элемент

CLS -> Очищает вывод

Page 35: Лекция о языке программирования Haskell

Интерпретатор скриптового языка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]

Page 36: Лекция о языке программирования Haskell

Интерпретатор скриптового языка

Интерпретатор №1:

Стек -> (функция) -> Стек’

Интерпретатор №2:

(Стек+Вывод) -> (функция) -> (Стек+Вывод)’

Состояние вычисления = Стек+Выводdata Result = State [Int] [Int]

Page 37: Лекция о языке программирования Haskell

Интерпретатор скриптового языка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

Page 38: Лекция о языке программирования Haskell

Интерпретатор скриптового языка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 [] [])

Page 39: Лекция о языке программирования Haskell

Язык \ подход

Ф-подход:данные -> (функция) -> данные’

ОО-подход:объект = данные + функции

Page 40: Лекция о языке программирования Haskell

Сравнение подходовЗадает ли язык программирования ограничение на парадигму?

Page 41: Лекция о языке программирования Haskell

Сравнение подходов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

Page 42: Лекция о языке программирования Haskell

Применимость подходовФ-подход:

Программа ориентирована на обработку (алгоритмы шифрования, эвалюаторы)

Легко изменяется функционал

ОО-подход:Программа ориентирована на данные (прикладные программы)Легко изменяются данные/источники

Page 43: Лекция о языке программирования Haskell

Дополнительная задача №1Есть фигуры: эллипс, квадрат, прямоугольник1. Построить иерархию классов2. Написать функцию, которая увеличивает ширину фигуры в x раз(площадь тоже увеличивиается в x раз)

ShapeEllipse Rectangle

Square

Page 44: Лекция о языке программирования Haskell

Дополнительная задача №1void enlarge(float coef)

vs

Shape enlarge(float coef)

Page 45: Лекция о языке программирования Haskell

Дополнительная задача №2void myownhugedataset::operation() {

try {

a = b;

c *= 2;

...

} catch(exception e) {

printf(":(");

//Как восстановить рабочее состояние объекта?

}

}

Page 46: Лекция о языке программирования Haskell

Дополнительная задача №2myownhugedataset myownhugedataset::operation() {

myownhugedataset copy = *this;

try {

copy = func1(copy);

...

} catch(exception e) {

return *this;

}

return copy;

}

Page 47: Лекция о языке программирования Haskell

Зачем стоит изучать Haskell