Funkcionlne programovanie 1 AIN51212 Peter Borovansk I18 http
Funkcionálne programovanie 1 -AIN-512/12 Peter Borovanský I-18 http: //dai. fmph. uniba. sk/courses/FPRO/
L. I. S. T.
Trochu z histórie FP n 1930, Alonso Church, lambda calculus n n n Princeton: A. Church, A. Turing, J. von Neumann, K. Gödel - skúmajú formálne modely výpočtov éra: WWII, prvý von Neumanovský počítač: Mark I (IBM), balistické tabuľky 1958, Haskell B. Curry, logika kombinátorov n n n teoretický základ FP kalkul funkcií: abstrakcia, aplikácia, kompozícia alternatívny pohľad na funkcie, menej známy a populárny „premenné vôbec nepotrebujeme“ 1958, LISP, John Mc. Carthy n n implementácia lambda kalkulu na „von Neumanovskom HW“ o. i. garbage collector pre Lisp Niektoré jazyky FP: n 1. frakcia: Lisp, Common Lisp, . . . , Scheme (MIT, Dr. Scheme, Racket) n 2. frakcia: Miranda, Erlang, Clean, Haskell Platform(Hugs), Scala, Ruby, …
1960 LISP n LISP je rekurzívny jazyk n LISP je vhodný na list-processing n LISP používa dynamickú alokáciu pamäte, GC n LISP je skoro beztypový jazyk n LISP používal dynamic scoping n LISP má globálne premenné, priradenie, cykly a pod. n ale nič z toho vám neukážem n LISP je vhodný na prototypovanie a je všelikde n Scheme je LISP dneška, má viacero implementácií, napr.
Scheme (Dr. Scheme, Dr. Racket) <Expr> : : = L je λ-term: L : : = x | (L L) | (λx L) • x je premenná • (L L) je aplikácia (funkcie) • (λx L) je λ-abstrakcia funkcie s argumentom <Const> | <Ident> | (<Expr 0> <Expr 1> … <Exprn>) | (lambda (<Ident 1>…<Identn>) <Expr>) | (define <Ident> <Expr>) definícia funkcie: (define gcd (lambda (a b) (if (= a b) a (if (> a b) (gcd (- a b) b) (gcd a (- b a)))))) volanie funkcie: (gcd 12 18) 6
Rekurzia na číslach (define fac (lambda (n) (if (= n 0) 1 (* n (fac (- n 1)))))) (fac 100) 933262. . 000 (define fib (lambda (n) (if (= n 0) 0 (if (= n 1) 1 (+ (fib (- n 1)) (fib (- n 2))))))) (fib 10) 55 (define ack (lambda (m n) (if (= m 0) (+ n 1) (if (= n 0) (ack (- m 1) 1) (ack (- m 1) (ack m (- n 1))))))) (ack 3 3) 61 (define prime (lambda (n k) (if (> (* k k) n) #t (if (= (remainder n k) 0) #f (prime n (+ k 1)))))) (define is. Prime? (lambda (n) (and (> n 1) (prime n 2))))
Dr. Scheme po inštalácii Dr. Scheme treba zvoliť si správnu úroveň jazyka (t. j. advanced user, resp. Essentials of PL) http: //www. plt-scheme. org/software/drscheme/tour-Z-H-4. html
Scheme closures (define (iterate n f) (if (= n 0) (lambda (x) x) (lambda (x) (f ((iterate (- n 1) f) x )) ) (define add. N 5 (lambda (n) (+ n 5))) (define add. N 1 (lambda (n) (+ n 1))) ((iterate 7 add. N 5 ) 100)
91 mc 91 n | n > 100 = n-10 | otherwise = mc 91 (n+11)) http: //en. wikipedia. org/wiki/John_Mc. Carthy_%28 computer_scientist%29
Lambda days http: //www. spoj. com/ n n n http: //lambdadays. sphere-contest. com/ http: //www. lambdadays. org/ http: //lambdadays. sphere-contest. com/problems
Funkcia (predikát) argumentom n zober zo zoznamu tie prvky, ktoré spĺňajú bool-podmienku (test) Booleovská podmienka príde ako argument funkcie a má typ (a -> Bool): filter p xs : : (a -> Bool) -> [a] = [ x | x <- xs, p x ] vlastnosti (zväčša úplne zrejmé): n filter True xs = xs n filter False xs = [] n filter p 1 (filter p 2 xs)) = filter (p 1 && p 2) xs n (filter p 1 xs) ++ (filter p 2 xs) = filter (p 1 || p 2) xs > filter even [1. . 10] [2, 4, 6, 8, 10]
map funktor, ktorý map f [] map f (x: xs) map f xs n aplikuje funkciu (1. argument) na všetky prvy zoznamu : : (a->b) -> [a] -> [b] = [] = f x : map f xs = [ f x | x <- xs ] Príklady: map (+1) [1, 2, 3, 4, 5] map odd [1, 2, 3, 4, 5] and (map odd [1, 2, 3, 4, 5]) = [2, 3, 4, 5, 6] = [True, False, True] = False map head [ [1, 0, 0], [2, 1, 0], [3, 0, 1] ] map tail [ [1, 0, 0], [2, 1, 0], [3, 0, 1] ] map (0: ) [[1], [2], [3]] = [1, 2, 3] = [ [0, 0], [1, 0], [0, 1] ] = [[0, 1], [0, 2], [0, 3]] n
map, filter a ich vlastnosti n n map id xs = xs map (f. g) xs = map f (map g xs) Na zamyslenie: n filter p (map f xs) = ? ? ? (filter (p. f) xs) = ? ? ? map f (filter (p. f) xs) filter p (map f xs) = [ f x | x<-xs, p (f x)] n map f (filter p xs) xs = ? ? ? filter p (map f xs) = [ f x | x<-xs, p x]
Haskell – foldr : : (a -> b) -> b -> [a] -> b foldr f z [] =z foldr f z (x: xs) = f x (foldr f z xs) a : b : c : [] -> f a (f b (f c z)) Main> foldr (+) 0 [1. . 100] 5050 Main> foldr (x y->10*y+x) 0 [1, 2, 3, 4] 4321 : f / / a : foldr f z a f / ----> / b : b f / / c [] c z -- g je vnorená lokálna funkcia foldr : : (a -> b) -> b -> [a] -> b foldr f z = g where g [] =z g (x: xs) = f x (g xs)
Haskell – foldl : : (a -> b -> a) -> a -> [b] -> a foldl f z [] = z foldl f z (x: xs) = foldl f (f z x) xs a : b : c : [] -> f (f (f z a) b) c Main> foldl (+) 0 [1. . 100] 5050 Main> foldl (x y->10*x+y) 0 [1, 2, 3, 4] 1234 : f / / a : foldl f z f c / ------> / b : f b / / c [] z a
foldr a foldl ešte raz n foldl (+) 0 [1, 2, 3, 4] foldr (+) 0 [1, 2, 3, 4] 10 n n foldr max (-999) [1, 2, 3, 4] foldl max (-999) [1, 2, 3, 4] 4 foldr (_ -> y ->(y+1)) 0 [3, 2, 1, 2, 4] foldl (x -> _ ->(x+1)) 0 [3, 2, 1, 2, 4] 5 n rozpoj : : [(a, b)] -> ([a], [b]) rozpoj = foldr ((x, y) -> (xs, ys) -> (x: xs, y: ys)) ([], []) rozpoj [(1, 11), (2, 22), (3, 33)] ([1, 2, 3], [11, 22, 33])
Vypočítajte n n n foldr max (-999) [1, 2, 3, 4] foldl max (-999) [1, 2, 3, 4] foldr (_ -> y ->(y+1)) 0 [3, 2, 1, 2, 4] foldl (x -> _ ->(x+1)) 0 [3, 2, 1, 2, 4] foldr (-) 0 [1. . 100] = (1 -(2 -(3 -(4 -. . . -(100 -0))))) = 1 -2 + 3 -4 + 5 -6 +. . . + (99 -100) = -50 n foldl (-) 0 [1. . 100] = (. . . (((0 -1)-2)-3). . . - 100) = -5050
Funkcia je hodnotou n n [a->a] je zoznam funkcií typu a->a napríklad: [(+1), (+2), (*3)] je [x->x+1, x->x+2, x->x*3] čo je foldr (. ) id [(+1), (+2), (*3)] ? ? akého je typu foldr (. ) id [(+1), (+2), (*3)] 100 foldl (. ) id [(+1), (+2), (*3)] 100 [a->a] 303 ? ? ? lebo skladanie fcií je asociatívne: n ((f. g). h) x = (f. g) (h x) = f (g (h x)) = f ((g. h) x) = (f. (g. h)) x n mapf : : [a->b] -> [a] -> [b] [] _ = [] _ [] = [] (f: fs) (x: xs) = (f x): mapf fs xs mapf [(+1), (+2), (+3)] [10, 20, 30] -- rovnako dlhé zoznamy [11, 22, 33]
Kvíz foldr (: ) [] xs = xs foldr (: ) ys xs = xs++ys foldr ? ? xs = reverse xs
Vlastnosti Fussion Law: Ak platí potom platí f z 1 = z 2 && f (g 1 a b) = g 2 a (f b) f. foldr g 1 z 1 = foldr g 2 z 2 Príklad použitia Fussion Law: (n*). foldr (+) 0 = foldr (+). (n*) 0 Dôkaz (pomocou Fussion Law): overíme predpoklady f = (n*), z 1 = z 2 = 0, g 1 = (+), g 1 = g 2 = (+). (n*) n n (n*) 0 = 0 (n*) (a+b) = (n*a + n*b) = (+). (n*) a ((n*) b)
Vlastnosti Acid Rain (fold/build/deforestation theorem) foldr f z. g (: ) [] = g f z Intuícia: Keď máme vytvoriť zoznam pomocou funkcie g zo zoznamových konštruktorov (: ) [], na ktorý následne pustíme foldr, ktorý nahradí (: ) za f a [] za z, namiesto toho môžeme konštruovať priamo výsledný zoznam pomocou g f z. Otypujme si to: Ak z : : u, potom f : : x->u->u, foldr f z : : [x]->u Ľavá strana: ([x]->u). (t->[x]) výsledkom je typ t->u Pravá strana: g : : (x -> u) -> u -> (t ->u)
length. map _ = length foldr f z. g (: ) [] = g f z map : : (a -> b) -> [a] -> [b] map h = foldr ((: ). h) [] -- (: ). h a as = (: )(h a as) = h a: as = (f z -> foldr (f. h) z) (: ) [] length : : [a] -> Int = foldr ( _ -> n -> n+1) 0 length. map h = length (foldr ( _ -> n -> n+1) 0). (foldr ((: ). h) []) = = podľa Acid Rain theorem (f = ( _ -> n -> n+1), z = 0, ale čo je g ? … g x y = (foldr (x. h) y) g f z = (foldr (f. h) z) = foldr (( _ ->n -> n+1). h) 0 = foldr ((_ ->n -> n+1) ) 0 = length lebo (( _->n ->n+1). h) x y = ( _->n->n+1) (h x) y = (n->n+1) y = y+1
foldr a foldl pre pokročilejších definujte foldl pomocou foldr, alebo naopak: : f / / a : foldr f z a f / ----> / b : b f / / c [] c z : f / / a : foldl f z f c / ------> / b : f b / / c [] z a (flip f) myfoldl f z xs = foldr (x -> y -> (f y x)) z (my. Reverse xs) myfoldr f z xs = foldl (x -> y -> (f y x)) z (my. Reverse xs) n n odstránime my. Reverse xs = foldr (x -> y -> (y ++ [x])) [] xs myfoldl' f z xs = foldr (x -> y -> (f y x)) z (foldr (x -> y -> (y ++ [x])) [] xs) odstránime ++ xs ++ ys = foldr (: ) ys xs myfoldl'' f z xs = foldr (x -> y -> (f y x)) z (foldr (x -> y -> (foldr (: ) [x] y)) [] xs) hmmm. . . , teoreticky (možno) zaujímavé, prakticky nepoužiteľné. . .
foldr a foldl posledný krát Zamyslime sa, ako z foldr urobíme foldl: induktívne predpokladajme, že rekurzívne volanie foldr nám vráti výsledok, t. j. hodnotu y, ktorá zodpovedá foldl: n y = myfoldl f [b, c] = z -> f (f z b) c nech x je ďalší prvok zoznamu, t. j. n x=a : f / / a : foldr f z a f / ----> / b : b f / / c [] c z : f / / a : foldl f z f c / ------> / b : f b / / c [] z a ako musí vyzerať funkcia ? , ktorou fold-r-ujeme, aby sme dostali myfoldl f [a, b, c] = z’ -> f (f (f z’ a) b) c = ? x y ? = (x y z’ -> y (f z’ x)) dosaďme: n (z’ -> (z -> f (f z b) c) (f z’ a)) = n (z’ -> f (f (f z’ a) b) c) = n z’ -> f (f (f z’ a) b) c n
Pre tých, čo neveria, fakt posledný krát ? = (x y z’ -> y (f z’ x)) n myfoldl''' f xs z = foldr (x y z -> y (f z x)) id xs z n myfoldl''' f [] = id n myfoldl''' f [c] = (x y z -> y (f z x)) c id = z -> f z c n n n myfoldl''' f [b, c] = (x y z -> y (f z x)) b (w -> f w c) = z -> (w -> f w c) (f z b) = z -> f (f z b) c myfoldl''' f [a, b, c] = (x y z -> y (f z x)) a (w -> f (f w b) c) = z -> (w -> f (f w b) c) (f z a) = z -> f (f (f z a) b) c myfoldl '''' f z xs = foldr (x y z -> y (f x z)) id xs z . . . doma skúste foldr pomocou foldl. . .
- Slides: 25