Upload
others
View
4
Download
0
Embed Size (px)
Citation preview
Ján Kollár Funkcionálne programovanie 1
Jazyky procedurálne, objektové, deklaratívne (aplikatívne, popisné) – logické, funkcionálne.
Čisté funkcionálne jazyky (bez procedurálnych prvkov) Orwell, Gofer, Hugs98, Haskell, Miranda.
Teoretický základ Moses Schönfinkel, Haskell Curry – kombinatorická logika, Alonzo Church – jazyk lambda (lambda kalkul) – základný,
rozšírený
Implementácia pomocou rozšíreného jazyka lambda (výrazy if, case, let, letrec) (Simon Peyton Jones, Philip Wadler, David Turner)
Procedurálna metodológia programovania popis problému, organizácia pamäti, organizácia postupnosti krokov.
Metodológia funkcionálneho programovania iba popis (formálny opis) problému. žiadne procedurálne prvky (premenné ako bunky,
priradenie, postupnosť krokov), matematický pojem premennej, funkcie vyššieho rádu.
Funkcionálny program je výraz – konštantná aplikatívna forma (CAF), množina rekurzívnych funkcií a hlavná funkcia – vždy konštanta.
Rozšírená štruktúra funkcionálneho programu modularita, import, export, riešenie kolízie mien, typové triedy a ich inštancie, ob-
sahujúce definície prekrývaných operácií (funkcií)
Typový polymorfizmus parametrický aj prekrývanie (overloading), statická kontrola typov.
Vykonávanie funkcionálneho programu postupná aplikácia funkcií na argumenty(currying), transformácia CAF – zjednodušenie –
redukcia (beta) výrazu lambda – výpočet, implicitná konštrukcia údajových štruktúr, automatické uvoľňovanie z pamäti (garbage
collection)
Vedľajší účinok (side effect) – dosiahnuteľný monadickými operáciami.
Praktický prístup k funkcionálnemu programovaniu návrh zhora-nadol, implementácia (definícia funkcií) zdola-nahor, overovanie
typov funkcií, aplikácia na vzorky argumentov, hľadanie čo nastručnejšej formy, využívajúc zameniteľnosť (konvertibilitu) výrazov,
funkcie vyššieho rádu, nerekurzívne definície a množinové abstrakcie. Podrobné komentovanie neformálneho významu argumentov a
hodnoty funkcie a uvádzanie testovacích aplikácií a ich hodnôt v dokumentácii.
Koncepcia funkcionálneho programovania
Ján Kollár Funkcionálne programovanie 2
data T α1 α2 . . . αu = C0 T0,1 T0,2 . . . T0,n0| C1 T1,1 T1,2 . . . T1,n1| C2 T2,1 T2,2 . . . T2,n2
... ... ... . . . ...
| Cm Tm,1 Tm,2 . . . Tm,nm
f :: T1 → T2 → . . .→ Tn → T
f x1 x2 . . . xn = e
f p1,1 p1,2 . . . p1,n = e1
f p2,1 p2,2 . . . p2,n = e2
· · · · · · · · · · · · · · · · · · · · · · · ·f pq,1 pq,2 . . . pq,n = eq
f m1 m2 . . . mn
T1 → T2 → . . .→ Tn → T ≡ T1 → (T2 → (. . .→ (Tn → T )) . . .))f m1 m2 . . . mn ≡ ( . . . ((f m1) m2) . . . ) mn
Základné prvky funkcionálneho jazyka
Ján Kollár Funkcionálne programovanie 3
add :: Int -> Int -> Int
add x y = x + y
add2 :: (Int,Int) -> Int
add2 (x,y) = x + y
add3 x y = x - y
globálna úroveň definícií funkcií, význam funkcie, premenné (parametre) funkcie, vzory, arita funkcie - konštanta, unárna a binárna funkcia,
funkcia n argumentov, mená funkcií, definícia (odvodenie) typu funkcie, operácie a typové operácie, definícia nového (algebraickeho) typu,
typ sumárny, násobný, nerekurzívny a rekurzívny, typ konštruktora, význam konštruktora, princíp postupnej aplikácie funkcie, asociatívnosť
operácie aplikácie, asociatívnosť typovej operácie zobrazenia, aplikácia konštruktora ako kanonickej funkcie, asociatívnosť typovej operácie
zobrazenia, princíp výpočtu ako postupného zjednodušenia CAF (redukcie výrazu lambda) do kanonickej formy, zameniteľnosť výrazov,
referenčná transparentnosť, hodnota (aplikácie) funkcie, funkcia ako hodnota, funkcie vyššieho rádu.
Základné pojmy
Ján Kollár Funkcionálne programovanie 4
add x y = x + y
square x = x * x
main = add 2 (square 3)
add 2 (square 3)
⇒ add 2 (3 ∗ 3)⇒ add 2 9⇒ 2 + 9⇒ 11
add 2 (square 3)
⇒ 2 + (square 3)⇒ 2 + (3 ∗ 3)⇒ 2 + 9⇒ 11
Výpočet na základe hodnoty a požiadavky (eager and lazy)
Ján Kollár Funkcionálne programovanie 5
module Main where
import Data.Char
add x y = x + y
square x = x * x
main = add 2 (square 3) -- main :: IO () -- pre GHC
main = let
add = \x->(\y->x+y)
square = \x->x*x
in (add 2 (square 3))
main = let add = \x->(\y->x+y); square = \x->x*x in (add 2 (square 3))
let x=2 in x*x = (\x->x*x) 2
let x=2; y=3 in x*y = LETREC x=2; y=3 in x*y
Ekvivalentné formy: program a LETREC, definícia funkcie pomocou abstrakcie lambda, významy let (Haskell)
Ján Kollár Funkcionálne programovanie 6
f (x,y,z) = (f x, g y, h z)
where
f = (2*)
g = (3*)
h = (4*)
f = \(x,y,z) -> let
f = \x -> (2*) x
g = \x -> (3*) x
h = \x -> (4*) x
in (f x, g y, h z)
Ekvivalentné formy: where a LETREC
Ján Kollár Funkcionálne programovanie 7
fac 0 = 1
fac (n+1) = (n+1) * fac n
fac n | n == 0 = 1
| n > 0 = n * fac (n-1)
fac n = if (n == 0) then 1 else (if (n > 0) then (n * fac (n-1)) else undefined)
Ekviv. formy: Porovnávanie vzorov pre celé nezáporné čísla (Hugs98), podmienené výrazy, if, otherwise, rekurzívna definícia
fac n | n == 0 = 1
| otherwise = n * fac (n-1)
fac 0 = 1
fac n = n * fac (n-1)
fac n = n * fac (n-1)
fac 0 = 1
Nesprávne definície: úplná namiesto neúplnej a nejednoznačná
Ján Kollár Funkcionálne programovanie 8
map f [] = []
map f (x:xs) = f x : map f xs
map = \f ->(\p->case p of
[] -> []
(x:xs) -> f x : map f xs)
map = \f ->(\p->case p of
[] -> []
(x:xs) -> ((\u->(\us->f u : map f us))) x xs)
Ekvivalentné formy: porovnávanie vzorov pre zoznamy, case, kolízia mien (map)
Ján Kollár Funkcionálne programovanie 9
module Sorting (iSort) where
{- Triedenie vkladanim (insertsort) -----------------------------------------
isort :: [a] -> [a]
iSort xs ... usporiadava zoznam prvkov xs, hodnotou je usporiadany zoznam prvkov
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr op a [x1,x2,...,xn] = x1 op (x2 op (... op (xn op a)...))
kde op=insert, a=[]
insert :: a -> [a] -> [a]
insert x xs ... vklada prvok x do prazdneho alebo usporiadaneho zoznamu xs tak,
aby hodnotou bol usporiadany zoznam
---------------------------------------------------------------------------- -}
-- iSort :: [a] -> [a]
iSort xs = foldr insert [] xs
where
insert x [] = [x]
insert x (y:ys) | x stringsort
"abgst"
> numssort
[1,3,4,6,7,8,9]
Príklad: Modul Sorting (sorting.hs), export iSort
Ján Kollár Funkcionálne programovanie 10
iSort :: Ord a => [a]->[a]
iSort = \xs -> let
insert = \x->(\u-> (case u of
[] -> [x]
(y:ys)-> (\y->(\ys->(if (x> (putStr.show) (S.iSort "abeceda")
Príklad: Modul Main (main.hs), kvalifikácia importovanej funkcie iSort, kompozícia a monadické operácie
Ján Kollár Funkcionálne programovanie 11
-- return :: a -> IO a
-- (>>=):: IO a -> (a -> IO b) -> IO b
-- (>>) :: IO a -> IO b -> IO b
-- 1.verzia
-- readNames :: [String] -> IO [String]
readNames xs = getLine >>= \s -> if s=="" then (return xs) else readNames (xs++[s])
-- 2.verzia
readNames xs = getLine >>= addName
where
addName s | s=="" = return xs
| otherwise = readNames (xs++[s])
-- 3.verzia
readNames xs = getLine >>= addName xs
addName xs s | s=="" = return xs
| otherwise = readNames (xs++[s])
-------------------------------------------------------------------
main = readNames []
{- test
*Main> main
alfa
beta
gamma
["alfa","beta","gamma"]
-- test zapisu do súboru: readNames [] >>= \xs -> writeFile "testfile.txt" (concat xs)
Aplikácia monadických operácií
Ján Kollár Funkcionálne programovanie 12
Asociatívnosť Operácia Význam
doprava ^, ^^, ** mocnina
doľava *, /, ‘div‘, ‘mod‘ aritmetické operácie
doľava +, - aritmetické operácie
doprava ++ zreťazenie
žiadna ==, /=, porovnanie
doprava && logický súčin
doprava || logický súčet
Tabuľka 1: Vybrané binárne operácie
Binárne operácie
Ján Kollár Funkcionálne programovanie 13
Ci :: T1,i → T1,i → . . .→ T1,ni → T α1 α2 . . . αu, i = 0 . . .m
m = 0⇒ násobný typ T
m > 0⇒ sumárny typ T
u = 0⇒ monomorfný typ T
u > 0⇒ polymorfný typ T
∀Ti,j ;T 6∈ Ti,j ⇒ T je nerekurzívny typ.
∃Ti,j ; T ∈ Ti,j ⇒ T je rekurzívny typ.
Jednoduché typy Char, Int, Float
Typ logických hodnôt. nerekurzívny typ. data Bool = False | True
Entice. nerekurzívny typ.
data Tuple2 a b = Tuple2 a b
data Tuple3 a b c = Tuple3 a b c
data Tuple4 a b c d = Tuple4 a b c d
Zoznamy. Zoznam (a String) je rekurzívny typ. data List a = Nil | Cons a (List a)
Celé nezáporné čísla. Rekurzívny typ. data Nat = Zero | Succ Nat
Klasifikácia typov a základné typy
Ján Kollár Funkcionálne programovanie 14
Pre entice
Typ Tuple2 a b (a, b), Tuple3 a b c (a, b, c), . . .
Konštruktor Tuple2 e1 e2 (e1, e2), Tuple3 e1 e2 e3 (e1, e2, e3), . . .
Pre zoznamy
Typ List a [a]
Konštruktory Nil [ ], Cons e1 e2 (e1 : e2), . . .
Syntaktická podpora zápisu entíc a zoznamov
Ján Kollár Funkcionálne programovanie 15
Enumeračný typ
data Farba = Fialova | Modra | Zelena | Zlta | Cervena
Variantný typ
data ZnakAleboCislo = Z Char | C Int
Binárny strom
data Btree a = Tip a | Bin (Btree a) (Btree a)
Binárny značkovaný strom
data Lbtree a b = Tip a | Bin b (Lbtree a b) (Lbtree a b)
Binárny vyhľadávací strom
data Bstree a = Tip | Bin a (Bstree a) (Bstree a)
Všeobecný strom
data Gtree a = Node a [Gtree a]
type String = [Char]
type Matrix a = [[a]]
type Graph a = [(a, [a])]
Niektoré užitočné typy a synonymá typov
Ján Kollár Funkcionálne programovanie 16
class Show a where
.................
typové signatúry
.................
data Farba = Biela | Modra
instance Show Farba where
show Biela = "Biela"
show Modra = "Modra"
data Farba = Biela | Modra deriving (Eq,Show)
Definícia resp. odvodenie operácií abstraktného typu pre nový algebraický typ
Ján Kollár Funkcionálne programovanie 17
fst (x,y) = x
snd (x,y) = y
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
reverseBtree (Tip x) = Tip x
reverseBtree (Bin tl tr) = Bin (reverseBtree tr)
(reverseBtree tl)
last [x] = x
last (x:x’:xs) = last (x’:xs)
length :: [a] -> Int
length [] = 0
length (x:xs) = 1 + length xs
(++) :: [a] -> [a] -> [a]
[] ++ ys = ys
(x:xs) ++ ys = x : xs ++ ys
concat :: [[a]] -> [a]
concat [] = []
concat (xs:xss) = xs ++ concat xss
Najdôležitejšie funkcie
Ján Kollár Funkcionálne programovanie 18
(!!) :: [a] -> Int -> a
(x:xs) !! 0 = x
(x:xs) !! (n+1) = xs !! n
head :: [a] -> a
head (x:xs) = x
tail :: [a] -> [a]
tail (x:xs) = xs
init :: [a] -> [a]
init [x] = []
init (x:y:xs) = x : init (y:xs)
last :: [a] -> a
last [x] = x
last (x:y:xs) = last (y:xs)
take :: Int -> [a] -> [a]
take 0 xs = []
take (n+1) [] = []
take (n+1) (x:xs) = x : take n xs
drop :: Int -> [a] -> [a]
drop 0 xs = xs
drop (n+1) [] = []
drop (n+1) (x:xs) = drop n xs
Najdôležitejšie funkcie
Ján Kollár Funkcionálne programovanie 19
copy :: Int -> a -> [a]
copy n x = take n xs where xs = x:xs
space :: Int -> String
space n = copy n ’ ’
map :: (a -> b) -> [a] ->[b]
map f [] = []
map f (x:xs) = f x : map f xs
filter :: (a -> Bool) -> [a] -> [a]
filter p [] = []
filter p (x:xs) | p x = x : filter p xs
| otherwise = filter p xs
even :: Int -> Bool
even x = (x ‘mod‘ 2) == 0
takeWhile :: (a -> Bool) -> [a] -> [a]
takeWhile p [] = []
takeWhile p (x:xs) | p x = x : takeWhile p xs
| otherwise = []
dropWhile :: (a -> bool) -> [a] -> [a]
dropWhile p [] = []
dropWhile p (x:xs) | p x = dropWhile p xs
| otherwise = x:xs
Najdôležitejšie funkcie
Ján Kollár Funkcionálne programovanie 20
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f a [] = a
foldl f a (x:xs) = foldl f (f a x) xs
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f a [] = a
foldr f a (x:xs) = f x (foldr f a xs)
sum :: Num a => [a] -> a
sum = foldl (+) 0
product :: Num a => [a] -> a
product = foldl (*) 1
concat :: [[a]] -> [a]
concat = foldr (++) []
and :: [Bool] -> Bool
and = foldr (&&) True
or :: [Bool] -> Bool
or = foldr (||) False
foldl1 :: (a -> a -> a) -> [a] -> a
foldl1 f (x:xs) = foldl f x xs
foldr1 :: (a -> a -> a) -> [a] -> a
foldr1 f [x] = x
foldr1 f (x:y:xs) = f x (foldr1 f (y:xs))
Najdôležitejšie funkcie
Ján Kollár Funkcionálne programovanie 21
minimum :: [a] -> a
minimum = foldl1 min
maximum :: [a] -> a
maximum = foldl1 max
scan :: (a -> b -> a) -> a -> [b] -> [a]
scan f a xs = a : sc f a xs
where
sc f a [] = []
sc f a (x:xs) = scan f (f a x) xs
zip :: [a] -> [b] -> [(a, b)]
zip [] ys = []
zip (x:xs) [] = []
zip (x:xs) (y:ys) = (x,y) : zip xs ys
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f [] [] = []
zipWith f [] (y:ys) = []
zipWith f (x:xs) [] = []
zipWith f (x:xs) (y:ys) = (f x y) : zipWith f xs ys
Najdôležitejšie funkcie
Ján Kollár Funkcionálne programovanie 22
(A) Pravidlo aplikácie:f x : T
x : T ′ ∧ f : T ′ → Tkde T ′ je nový typ.
(R) Pravidlo rovnosti:x : T ∧ x : T ′
T = T ′
(F) Pravidlo funkcie:T → U = T ′ → U ′
T = T ′ ∧ U = U ′T T1 . . . Tn = T T ′1 . . . T
′n
T1 = T ′1 ∧ . . . ∧ Tn = T ′n(.) :: (b -> c) -> (a -> b) -> a -> c
(f . g) x = f (g x)
(◦) f g x = f (g x)(◦) : T1 → T2 → T3 → T4f : T1
g : T2
x : T3
f (g x) : T4
1. Poďľa (A): f (g x) : T4 ⇒ (g x) : T5 ∧ f : T5 → T4 .
2. Poďľa (R): f : T1 ∧ f : T5 → T4 ⇒ T1 = T5 → T4.
3. Poďľa (A): (g x) : T5 ⇒ x : T6 ∧ g : T6 → T5.
4. Poďľa (R): g : T2 ∧ g : T6 → T5 ⇒ T2 = T6 → T5.
5. Poďľa (R): x : T3 ∧ x : T6 ⇒ T3 = T6 = T7.
(◦) : (T5 → T4)→ (T7 → T5)→ T7 → T4
(.) :: (b -> a) -> (c -> b) -> c -> a
Pravidlá typovej kontroly a odvodenie typu kompozície
Ján Kollár Funkcionálne programovanie 23
f x = x x
T1 = T1 → T2.
f x y = fst x + fst y
fst :: (a,b) -> a
fst (x,y) = x
f x y = fst1 x + fst2 y
(+) : Int→ Int→ Intf : T1 → T2 → T3fst1 : (T4, T5)→ T4fst2 : (T6, T7)→ T6
x : T1
y : T2
((+) (fst1 x))(fst2 y) : T3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
f :: (Int, a)→ (Int, b)→ Int
(+)::Num a => a -> a -> a
f :: Num a => (a,b) -> (a,c) -> a
Odvodenie polymorfného typu – zlyhanie a viacnásobná aplikácia funkcie (konštruktora)
Ján Kollár Funkcionálne programovanie 24
any :: (a -> Bool) -> [a] -> Bool
any p = or . map p
all :: (a -> Bool) -> [a] -> Bool
all p = and . map p
elem :: a -> [a] -> Bool
x ‘elem‘ xs = any (x ==) xs
reverse :: [a] -> [a]
reverse = foldl (flip (:)) []
sp = sum . zipWith (*)
flip :: (a -> b -> c) -> b -> a -> c
flip f x y = f y x
Nerekurzívne definície a funkcie vyššieho rádu
Ján Kollár Funkcionálne programovanie 25
1. [e1..] generuje nekonečný zoznam počnúc od hodnoty výrazu e1 s krokom +1.
2. [e1, e2..] generuje nekonečný zoznam počnúc od hodnoty výrazu e1 s krokom e2 − e1.
3. [e1..e3] generuje konečný zoznam počnúc od hodnoty výrazu e1 v intervale 〈e1, e3〉 s krokom +1.
4. [e1, e2..e3] generuje konečný zoznam počnúc od hodnoty výrazu e1 v intervale 〈e1, e3〉 s krokom s krokom e2 − e1.
[ E | Q1, . . . , Qn ] :: [T ], ak E :: T
MA ::= [ E | QS ]
QS ::= Q1, . . . , Qn, pre n ≥ 0Q ::= G | FG ::= p
Ján Kollár Funkcionálne programovanie 26
E [[ [ E | qs ] ]] = C [[ [ E | qs ] ]]
C [[ [ E | ] ]] = [ E [[ E ]] ] (1)C [[ [ E | F, qs ] ]] = if (E [[ F ]] ) (C [[ [ E | qs ] ]] ) ([ ]) (2)C [[ [ E | F ] ]] = if (E [[ F ]] ) (C [[ [ E | ] ]] ) ([ ]) (3)C [[ [ E | p← L, qs ] ]] = h (λp. C [[ [ E | qs ] ]] ) (E [[ L ]] ) (4)C [[ [ E | p← L ] ]] = h (λp. C [[ [ E | ] ]] ) (E [[ L ]] ) (5)
Obr. 2: Schéma prekladu množinových abstrakcií
kde qs je zoznam kvalifikátorov a h je
h :: (a -> [b]) -> [a] -> [b]
h f [] = []
h f (x:xs) = f x ++ h f xs
Platí
h f = concat . (map f)
Preklad množinových abstrakcií
Ján Kollár Funkcionálne programovanie 27
concat :: [[a]] -> [a]
concat xss = [x | xs [a] ->[b]
map f xs = [f x | x c) -> [a] -> [b] -> [c]
zipWith f xs ys = [f x y | (x,y)
Ján Kollár Funkcionálne programovanie 28
data Btree a = Tip a | Bin (Btree a) (Btree a)
size (Tip x) = 1
size (Bin t1 t2) = size t1 + size t2
nsize (Tip x) = 0
nsize (Bin t1 t2) = 1 + size t1 + size t2
height (Tip x) = 0
height (Bin t1 t2) = 1 + (height t1) ‘max‘ (height t2)
mapBtree :: (a->b) -> Btree a -> Btree b
mapBtree f (Tip x) = Tip (f x)
mapBtree f (Bin t1 t2) = Bin (mapBtree f t1) (mapBtree f t2)
foldBtree :: (a->a->a) -> Btree a -> a
foldBtree op (Tip x) = x
foldBtree op (Bin t1 t2) = (foldBtree op t1) ‘op‘ (foldBtree op t2)
sumtips = foldBtree (+)
size = foldBtree (+) . mapBtree (const 1)
const :: a -> b -> a
const k x = k
height = foldBtree op . mapBtree (const 0)
where d1 ‘op‘ d2 = 1 + (d1 ‘max‘ d2)
tips = foldBtree (++) . mapBtree unit
where unit x = [x]
data LBtree a b = Tip a | Bin b (LBtree a b) (LBtree a b) -- binarny znackovany strom
Binárne stromy
Ján Kollár Funkcionálne programovanie 29
data BStree a = Nil | Bin a (BStree a) (BStree a)
empty :: BStree a
insert :: a -> BStree a -> BStree a
delete :: a -> BStree a -> BStree a
member :: a -> BStree a -> Bool
Binárne vyhľadávacie stromy a množinové operácie
Ján Kollár Funkcionálne programovanie 30
empty = Nil
insert x Nil = Bin x Nil Nil
insert x (Bin y t1 t2) | x < y = Bin y (insert x t1) t2
| x == y = Bin y t1 t2
| x > y = Bin y t1 (insert x t2)
delete x Nil = Nil
delete x (Bin y t1 t2) | x < y = Bin y (delete x t1) t2
| x == y = join t1 t2
| x < y = Bin y t1 (delete x t2)
join t1 t2 | t1 == Nil = t2
| otherwise = split t1 t2
split (Bin x t1 t2) t3 | t2 == Nil = Bin x t1 t3
| otherwise = Bin x t1 (split t2 t3)
member x Nil = False
member x (Bin y t1 t2) | x < y = member x t1
| x == y = True
| x > y = member x t2
labels Nil = []
labels (Bin x t1 t2) = labels t1 ++ [x] ++ labels t2
Implementácia množinových operácií
Ján Kollár Funkcionálne programovanie 31
sl Nil = 0
sl (Bin x t1 t2) = height t1 - height t2
Teraz možno definovať funkciu rebal pre vyváženie stromu, pričom predpokladáme, že jeho podstromy sú vyvážené:
rebal t | sl t == 2 = shiftr t
| sl t == -2 = shiftl t
| otherwise = t
Funkcie shiftr a shiftl definujú posun stromu doprava, resp. ďoľava pomocou rotácií nasledovne:
shiftr (Bin x t1 t2) | sl t1 == -1 = rotr (Bin x (rotl t1) t2)
| otherwise = rotr (Bin x t1 t2)
shiftl (Bin x t1 t2) | sl t2 == 1 = rotl (Bin x t1 (rotr t2))
| otherwise = rotl (Bin x t1 t2)
rotr (Bin x (Bin y t1 t2) t3) = Bin y t1 (Bin x t2 t3)
rotl (Bin x t1 (Bin y t2 t3)) = Bin y (Bin x t1 t2) t3
insert x Nil = Bin x Nil Nil
insert x (Bin y t1 t2) | xy = rebal (Bin y t1 (insert x t2))
Vyvážené stromy
Ján Kollár Funkcionálne programovanie 32
data Gtree a = Node a [Gtree a]
size (Node x gs) = 1 + sum (map size gs)
height (Node x []) = 0
height (Node x (g:gs)) = 1 + maximum (map height (g:gs))
mapGtree :: (a -> b) -> Gtree a -> Gtree b
mapGtree f (Node x gs) = Node (f x) (map (mapGtree f) gs)
foldGtree :: (a -> a -> a) -> Gtree a -> a
foldGtree f (Node x gs) = foldl f x (map (foldGtree f) gs)
Všeobecné stromy
Ján Kollár Funkcionálne programovanie 33
abstr :: R→ A
abstr :: List a→ [a]abstr Nil = [ ]
abstr (Cons x xs) = x : abstr xs
abstr :: Nat→ Intabstr Zero = 0
abstr (Succ x) = abstr x + 1
valid :: R→ Bool
abstr :: [a]→ Set aabstr [ ] = {}abstr (x : xs) = {x} ∪ abstr xs
valid1 xs = True
valid2 xs = nodups xs
valid3 xs = nodecs xs
Abstraktné typy – abstrakčná funkcia a predikát platnosti
Ján Kollár Funkcionálne programovanie 34
addset :: a→ Set a→ Set aaddset x s = {x} ∪ s
abstr (insert x xs) = addset x (abstr xs)
abstr ◦ (insert x) = (addset x) ◦ abstr
(abstr, valid1)
(abstr, valid2)
(abstr, valid3)
insert1 x xs = [x]
insert2 x xs = [x] ++ [y | y
Ján Kollár Funkcionálne programovanie 35
start :: Queue a
join :: Queue a→ a→ Queue afront :: Queue a→ areduce :: Queue a→ Queue a
(1)
front (join start x) = x (2)
front (join (join q x) y) = front (join q x) (3)
reduce (join start x) = start (4)
reduce (join (join q x) y) = join (reduce (join q x)) y (5)
Algebraická špecifikácia abstraktného typu front (Queue)
Ján Kollár Funkcionálne programovanie 36
data QUEUE a = Start | Join (QUEUE a) a
abstr :: QUEUE a→ Queue aabstr Start = start
abstr (Join q x) = join (abstr q) x
type QUEUE a = [a]
abstr :: QUEUE a→ Queue aabstr [ ] = start
abstr (x : xs) = join (abstr xs) x
Príklad voľby rôznej reprezentácie údajov
Ján Kollár Funkcionálne programovanie 37
type QUEUE a = [a]
class Queue a where
start :: QUEUE a
join :: QUEUE a -> a -> QUEUE a
front :: QUEUE a -> a
reduce :: QUEUE a -> QUEUE a
isEmpty :: QUEUE a -> Bool
mkqueue :: [a] -> QUEUE a
instance Queue Char where
start = []
join q x = q ++ [x]
front q = head q
reduce q = tail q
isEmpty [] = True
isEmpty (x:xs) = False
mkqueue [] = start
mkqueue (x:xs) = join (mkqueue (init (x:xs)))
(last (x:xs))
instance Queue Int where
start = []
join q x = q ++ [x]
front q = head q
reduce q = tail q
isEmpty [] = True
isEmpty (x:xs) = False
mkqueue [] = start
mkqueue (x:xs) = join (mkqueue (init (x:xs)))
(last (x:xs))
instance Queue (a,b) where
start = []
join q x = q ++ [x]
front q = head q
reduce q = tail q
isEmpty [] = True
isEmpty (x:xs) = False
mkqueue [] = start
mkqueue (x:xs) = join (mkqueue (init (x:xs)))
(last (x:xs))
Implementácia abstraktného typu – typová trieda a množina inštancií
Ján Kollár Funkcionálne programovanie 38
type QUEUE a = [a]
class Queue a where
start :: QUEUE a
join :: QUEUE a -> a -> QUEUE a
front :: QUEUE a -> a
reduce :: QUEUE a -> QUEUE a
isEmpty :: QUEUE a -> Bool
mkqueue :: [a] -> QUEUE a
-- definicia operacii pre vsetky instancie
start = []
join q x = q ++ [x]
front q = head q
reduce q = tail q
isEmpty [] = True
isEmpty (x:xs) = False
mkqueue [] = start
mkqueue (x:xs) = join (mkqueue (init (x:xs)))
(last (x:xs))
instance Queue Int
instance Queue Char
instance Queue (a,b)
instance Queue Bool
> start::QUEUE Int
[]
> start::QUEUE (Int,Char)
[]
> start::QUEUE Char
""
> reduce (join (join ( join (start::QUEUE Char) ’a’) ’b’) ’c’)
"bc"
> front (join (join ( join (start::QUEUE Char) ’a’) ’b’) ’c’)
’a’
> reduce (join (join ( join (start::QUEUE Int) 10) 20) 30)
[20,30]
> front (join (join ( join (start::QUEUE Int) 10) 20) 30)
10
>
Špeciálny prípad implementácie abstraktného typu v jazyku Haskell a verifikácia
Ján Kollár Funkcionálne programovanie 39
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
instance Monad IO where
(>>=) = primbindIO
return = primretIO
return :: a -> IO a
(>>=) :: IO a -> (a -> IO b) -> IO b
1. p0 >>= \x1->p1 >>= \x2->p2 >>= . . . >>= \x(n−1)->p(n−1) >>= \xn->pn
2. do { x1 m a -> m b -> m b
p >> q = p >>= \_ -> q
(>>) :: IO a -> IO b -> IO b
1. p0 >> p1 >> p2 >> . . . >> p(n−1) >> pn
2. p0 >>= \ ->p1 >>= \ ->p2 >>= . . . >>= \ ->p(n−1) >>= \ ->pn
3. do { p0 ; p1 ; . . . ; p(n−1) ; pn }
Používať formu zápisov 1., v žiadnom prípade nie do!!!
Monadické operácie return, (>>=) a (>>) a ekvivalentné monadické väzby
Ján Kollár Funkcionálne programovanie 40
getChar :: IO Char
getCh :: IO Char
getLine :: IO String
putChar :: Char -> IO ()
putStr :: String -> IO ()
putStrLn :: String -> IO ()
readFile :: FilePath -> IO String
writeFile :: FilePath -> String -> IO ()
appendFile :: FilePath -> String -> IO ()
readBinaryFile :: FilePath -> IO String
writeBinaryFile :: FilePath -> String -> IO ()
appendBinaryFile :: FilePath -> String -> IO ()
import IOExtensions - len pre Hugs98
main = putStrLn "Ahoj, ako sa volas?" >>= \x ->
getLine >>= \meno ->
putStrLn ("Ahoj " ++ meno ++ "!")
> main
Ahoj, ako sa volas?
Jano
Ahoj Jano!
>
Monadické funkcie pre vstup a výstup, príklad použitia
Ján Kollár Funkcionálne programovanie 41
Dokázať platnosť tvrdenia indukciou na zoznamoch znamená riešiť dva prípady:
Prípad [ ]. Dokázať, že platí tvrdenie T([ ]).
Prípad (x : xs). Dokázať nasledovné: Ak platí tvrdenie T(xs), potom platí aj tvrdenie T(x : xs).
Príklad – dôkaz platnosti vzťahu:
take n xs ++ drop n xs = xs
Prípad 0, xs.take 0 xs ++ drop 0 xs = [ ] ++ xs (take.1, drop.1)
= xs (++.1)
Pre tento prípad vzťah platí.
Prípad n+ 1, [ ].
take (n+ 1) [ ] ++ drop (n+ 1) [ ] = [ ] ++ [ ] (take.2 , drop.2 )
= [ ] (++.1)
Aj pre tento prípad vzťah platí.
Prípad n+ 1, (x : xs).
take (n+ 1) (x : xs) ++ drop (n+ 1) (x : xs)
= (x : take n xs) ++ drop n xs (take.3 , drop.3 )
= x : (take n xs ++ drop n xs) (++.2)
= x : xs (predpoklad)
Vzhľadom na to, že vzťah platí aj pre tretí prípad, platí pre všetky prípustné hodnoty a teda je platný.
�
Dokazovanie správnosti: dôkaz indukciou na zoznamoch
Ján Kollár Funkcionálne programovanie 42
data A a = N a | S (A a) (A a) . . . (A a)
Dokázať platnosť tvrdenia štrukturálnou indukciou znamená riešiť dva prípady:
Prípad (N x). Dokázať, že platí tvrdenie T(N x).
Prípad (N x1 x2 . . . xn). Dokázať nasledovné: Ak platia tvrdenia T(x1), T(x2), . . . T(xn), potom platí aj tvrdenie T(N x1 x2 . . . xn).
Príklad – dôkaz platnosti
size t ≤ 2ˆheight t
Platí:size (Tip x) = 1
size (Bin t1 t2) = size t1 + size t2
height (Tip x) = 0
height (Bin t1 t2) = 1 + (height t1) max (height t2)
Dôkaz:
Prípad (Tip x). Platí:
size (Tip x) = 1 (size.1)
= 2ˆ0 ( .̂1)
= 2ˆheight (Tip x) (height.1)
v súlade s predpokladom.
Dokazovanie správnosti: dôkaz štrukturálnou indukciou
Ján Kollár Funkcionálne programovanie 43
Prípad (Bin t1 t2).
Predpokladajme, vzhľadom na indukciu, že platí:
size t1 ≤ 2ˆheight t1size t2 ≤ 2ˆheight t2
a označme
d = (height t1) max (height t2)
Potom platí:size (Bin t1 t2)
= size t1 + size t2 (size.2)
≤ (2ˆheight t1) + (2ˆheight t2) (predpoklad)≤ (2ˆd) + (2ˆd) (dosadenie)= 2 (̂1 + d) (výpočet)
= 2 (̂height (Bin t1 t2)) (height.2)
�
Dokazovanie správnosti: dôkaz štrukturálnou indukciou – pokračovanie
Ján Kollár Funkcionálne programovanie 44
init xs = take (length xs− 1) xs, pre xs 6= [ ] (6)
Vzťah (6) je špecifikáciou a zároveň neefektívnou implementáciou. Efektívnejšiu implementáciu odvodíme tým, že odstránime
funkcie take a length.
(length) :: [a]→ Intlength [ ] = 0
length (x : xs) = 1 + length xs
Prípad [x].
init [x] = take (length [x] − 1) [x] (dosadenie)= take (1 + length [] − 1) [x] (length.2)= take (1 + 0 − 1) [x] (length.1)= take 0 [x] (výpočet)
= [ ] (take.1)
Výsledkom je rovnica
init [x] = [ ]
Indukčný predpoklad je
init (x : xs) = take (length (x : xs)− 1) (x : xs)
Vzťah syntézy a dokazovania správnosti: syntéza
Ján Kollár Funkcionálne programovanie 45
Prípad (x : y : xs).
init (x : y : xs) = take (length (x : y : xs) − 1) (x : y : xs) (dosadenie)= take (1 + length (y : xs) − 1) (x : y : xs) (length.2)= take (length (y : xs)) (x : y : xs) (výpočet)
= x : take (length (y : xs)− 1) (y : xs) (take.2)= x : init (y : xs) (predpoklad)
Druhá rovnica implementácie (pre viac ako jednoprvkový zoznam) je v tvare:
init (x : y : xs) = x : init (y : xs)
takže odvodená implementácia v tvare výslednej rekurzívnej definície funkcie init je nasledovná:
init [x] = [ ]
init (x : y : xs) = x : init (y : xs)
Vzťah syntézy a dokazovania správnosti: syntéza – pokračovanie
Ján Kollár Funkcionálne programovanie 46
Prepokladajme teraz, že sme uvedenú funkciu neodvodili, ale priamo ju navrhli. V tom prípade prichádza do úvahy dôkaz platnosti
tejto implementácie vo vzťahu na špecifikáciu (6), ktorý spočíva v dôkaze vzťahov
init [x] = [ ], pre length [x] = 1
init (x : y : xs) = x : init (y : xs) pre length (x : y : xs) > 1
Dôkaz:
Prípad [x]. – pre jednoprvkové zoznamy
init [x] = [ ] (dosadenie)
= take 0 [x] (take.1)
= take (length []) [x] (length.1 )
= take ((1 + length []) − 1) [x] (úprava)= take (length [x] − 1) [x] (length.2)
Implementácia teda zodpovedá špecifikácii pre jednoprvkové zoznamy.
Prípad (x : y : xs). – pre všetky viac ako jednoprvkové zoznamy
Indukčný predpoklad:
init (x : xs) = take (length (x : xs)− 1) (x : xs)
init (x : y : xs) = x : init (y : xs) (dosadenie)
= x : take (length (y : xs)− 1) (y : xs) (predpoklad)= take (length (y : xs)) (x : y : xs) (take.2)
= take ((1 + length (y : xs)) − 1) (x : y : xs) (úprava)= take (length (x : y : xs) − 1) (x : y : xs) (length.2)
Implementácia teda zodpovedá špecifikácii aj pre viac ako jednoprvkové zoznamy, a teda je správna.
�
Záver: Dôkaz platnosti pre program syntetizovaný matematickými metódami nemá význam robiť.
Vzťah syntézy a dokazovania správnosti: dôkaz platnosti
Ján Kollár Funkcionálne programovanie 47
Definícia 1: Abstraktná syntax jazyka lambda
E ::= k konštanta
| v meno premennej| E E aplikácia| λv.E abstrakcia lambda
incr x = x+ 1
(λ(x).(((+) (x)) (1)))
add x y = x+ y
(λ(x).(λ(y).(((+) (x)) (y))))
incr = λx.+ x 1
add = λx.λy.+ x y
Syntax jazyka lambda a príklady vyjadrenia funkcií v jazyku lambda
Ján Kollár Funkcionálne programovanie 48
Definícia 2: Voľná premenná
1. Premenná x je voľná v premennej x (nie však v konštante alebo inej premennej).
2. Premenná x je voľná v aplikácii (E F ) práve vtedy, ak je voľná vo výraze E alebo je voľná vo výraze F .
3. Premenná x je voľná v abstrakcii (λy.E) práve vtedy, ak x a y sú rôzne premenné a x je voľná v E.
Definícia 3: Viazaná premenná
1. Premenná x je viazaná v aplikácii (E F ) práve vtedy, ak je viazaná v E a zároveň je viazaná v F .
2. Premenná x je viazaná v abstrakcii (λy.E) práve vtedy, ak x a y sú tie isté premenné a x je voľná v E alebo ak x je viazaná
v E.
Vo výraze
(λx.+ x y) 4
je premenná x viazaná, y je voľná.
Operačná sémantika jazyka lambda: voľné a viazané premenné
Ján Kollár Funkcionálne programovanie 49
Definícia 4: Substitúcia E[M/x]
Ak x, y a z sú premenné, c je premenná alebo konštanta rôzna od x, a E, F a M sú výrazy lambda, potom substitúcia E[M/x] je
definovaná nasledovne:x [M/x] = M
c [M/x] = c
(E F ) [M/x] = E[M/x] F [M/x]
(λx.E) [M/x] = λx.E
(λy.E) [M/x] = λy.E[M/x] , ak x nie je voľná v E
alebo y nie je voľná v M
= λz.(E[z/y]) [M/x] , v opačnom prípade,
, kde z je nová premenná,
ktorá nie je voľná v E a M
Operačná sémantika jazyka lambda: substitúcia E[M/x]
Ján Kollár Funkcionálne programovanie 50
Definícia 5: Zámena alfa
Ak y nie je voľná v E, potom platí:
(λx. E)↔ λy. E[y/x]
Definícia 6: Zámena beta
(λx.E) M ↔ E[M/x]
Definícia 7: Zámena eta
Ak x nie je voľná v E a E je funkcia, potom:
(λx.E x)↔ E
Zámeny beta a eta použité zľava doprava (→) sa nazývajú redukcie.Predpoklad zámeny eta dovoľuje napríklad redukciu eta, ktorou je odstránená nadbytočná abstrakcia lambda
(λx. + 1 x)→ + 1
lebo (+ 1) je funkcia, ktorá neobsahuje x. Na druhej strane redukcia eta
(λx. + x x)→ + x
nie je platná, lebo (+ x) je síce funkcia, ale obsahuje x.
Operačná sémantika jazyka lambda: pravidlá zameniteľnosti výrazov
Ján Kollár Funkcionálne programovanie 51
1. Redukcia beta:
(λx1.λx2 . . . λxn.E) F → λx2 . . . λxn. E[F/x1]
kde E[F/x1] označuje výraz E, v ktorom všetky výskyty premennej x1 sú nahradené výrazom F , je platná, ak voľné premenné
argumentu F nie sú identické so žiadnym z formálnych parametrov x1, x2, . . . , xn tela E abstrakcie lambda:
λx1.λx2 . . . λxn. E
2. Zámena alfa:
λx. E ↔ λy. E[x/y]
je platná, ak nové meno y nie je identické s voľnou premennou výrazu E.
(λx.λy. + x y) y 2
→ (λy. + y y) 2 redukcia beta a kolízia y→ + 2 2→ 4
je chybný výsledok.
Operačná sémantika jazyka lambda: problém kolízie mien
Ján Kollár Funkcionálne programovanie 52
Veta 1: Church-Rosserova veta I
Ak E1 ↔ E2, potom existuje výraz E taký, že platí:
E1 → E a súčasne E2 → E
Žiadny výraz nemožno redukovať do dvoch rôznych normálnych foriem (t.j. alfa-nekonvertibilných).
Dôkaz:
Predpokladajme, že E ↔ E1 a E ↔ E2, kde E1 a E2 sú v normálnej forme. Potom E1 ↔ E2 a podľa Church-Rosserovej vety I musíexistovať výraz F taký, že platí:
E1 → F a súčasne E2 → F
Vzhľadom na to, že E1 a E2 neobsahujú žiadny redukovateľný výraz, platí E1 = F = E2. �
Veta 2: Church-Rosserova veta II
Ak E1 → E2, a E2 je v normálnej forme, potom existuje normálne poradie postupnosti redukcií z E1 do E2.
Normálne poradie redukcie určuje, že prvý bude redukovaný vonkajší (najvrchnejší) výraz zľava, napr.:
(λx.(λy.(∗ x y))) (+ 3 4) (− 5 2)→ (λy.(∗ (+ 3 4) y)) (− 5 2)→ (∗ (+ 3 4) (− 5 2))→ (∗ 7 (− 5 2))→ (∗ 7 3)→ 21
Church-Rosserove vety a normálne poradie redukcie
Ján Kollár Funkcionálne programovanie 53
Vonkajšia redukcia zľava. Je to normálne poradie redukcie.
Vnútorná redukcia zľava. Výber redukovateľného výrazu sa uskutočňuje sekvenčne zľava, avšak vyžaduje sa, aby aj funkcia aj
jej argument boli v normálnej forme.
Vonkajšia paralelná redukcia. Všetky redukovateľné výrazy, ktoré nie sú vnorené v iných výrazoch, sú vybraté naraz a naraz sú
redukované.
Vnútorná paralelná redukcia. Všetky redukovateľné výrazy, ktoré neobsahujú redukovateľné výrazy sú vyberané a redukované
súčasne.
Úplná paralelná redukcia. Všetky redukovateľné výrazy sú redukované naraz. Táto stratégia je veľmi komplikovaná, keďže výrazy
môžu byť vnorené.
Ak aj existuje normálna forma, vnútornou stratégiou redukcie nie je vždy dosiahnuteľná, t.j. výpočet nemusí skončiť. Ako príklad
možno uviesť vnútornú redukciu:
(λy. c) ((λx. x x x) (λx. x x x))
→ (λy. c) ((λx. x x x) (λx. x x x) (λx. x x x))→ (λy. c) ((λx. x x x) (λx. x x x) (λx. x x x) (λx. x x x))
ktorá vedie k expanzii výrazu
((λx. x x x) (λx. x x x))
pričom pri vonkajšej redukcii možno okamžite v prvom kroku získať normálnu formu c.
(λy. c) ((λx. x x x) (λx. x x x))
→ c
Stratégie redukcie výrazu
Ján Kollár Funkcionálne programovanie 54
Definícia 8: Slabá prefixná normálna forma
Výraz lambda je v slabej prefixnej normálnej forme práve vtedy, keď je v tvare:
F E1 E2 . . . En
kde n ≥ 0 a F je buď premenná alebo údajový objekt, alebo F je abstrakcia lambda alebo operátor a zároveň F E1E2 . . . Em nieje redukovateľný výraz pre m ≤ n.
Výraz neobsahuje žiadny vrcholový redukovateľný výraz vtedy a len vtedy, ak je v slabej prefixnej normálnej forme.
Definícia 9: Prefixná normálna forma
Výraz lambda je v prefixnej normálnej forme práve vtedy, keď je v tvare:
λx1.λx2. . . . λxn. v M1M2 . . . Mm
kde n,m ≥ 0 a v je premenná xi, údajový objekt alebo operátor a (v M1 M2 . . . Mp) nie je redukovateľný výraz pre p ≤ m.
Každý výraz v prefixnej normálnej forme je zároveň výrazom v slabej prefixnej normálnej forme, ale nie naopak.
Normálne formy výrazu
Ján Kollár Funkcionálne programovanie 55
Funkcia f :
f = λx. E
je rekurzívna, ak obsahuje premennú f v svojom tele E. Pomocou abstrakcie beta ju možno vyjadriť nasledovne:
f = (λx. E)→ (λn.λx. E[n/f ]) f = H f
kde
H = λn.λx. E[n/f ]
je abstrakcia lambda, ktorá neobsahuje f vo výraze E[n/f ], je to teda nerekurzívna funkcia. Potom rovnica:
f = H f (7)
znamená, že ak H sa aplikuje na f , výsledok ostáva f , a preto je f pevným bodom funkcie H. Funkcia môže mať aj viac pevných
bodov, napr. 0 a 1 sú pevné body funkcie
λx. ∗ x x
Funkcia Y , nazývaná tiež kombinátorom pevného bodu je funkcia, ktorá vypočíta pevný bod, keď je aplikovaná na H (pretože
pevný bod f závisí jedine od H). Teda platí:
f = Y H (8)
a po dosadení do rovnice (7)
Y H = H (Y H) (9)
Pritom kombinátor pevného bodu Y je definovaný nasledovne:
Y = (λh.(λx. h (x x)) (λx. h (x x))) (10)
Dosadením do (9) možno preveriť správnosť definície (10).
Vyjadrenie rekurzívnych funkcií v netypovanom jazyku lambda
Ján Kollár Funkcionálne programovanie 56
Definícia kombinátora Y je nerekurzívna a teda umožňuje vyjadriť rekurzívnu funkciu f pomocou aplikácie nerekurzívneho
kombinátora pevného bodu Y na funkciu H, ktorá je tiež nerekurzívna. Napr. rekurzívnu funkciu
fac k = 1, if k = 0
= k ∗ fac (k − 1), otherwise
vyjadrenú vo formálne nesprávnom (a rekurzívnom) tvare
fac = λk. IF (= k 0) 1 (∗ k (fac (− k 1)))
je možné vyjadriť správne a nerekurzívne nasledovne:
fac = (λh. (λx. h (x x)) (λx. h (x x)))
(λn.λk. IF (= k 0) 1 (∗ k (n (− k 1))))
Implementácia rekurzívnych funkcií na báze rozšíreného jazyka lambda nevyžaduje uplatnenie kombinátora Y .
Vyjadrenie rekurzívnych funkcií v netypovanom jazyku lambda – pokračovanie
Ján Kollár Funkcionálne programovanie 57
Eval [[ k ]] hodnota konštanty
Eval [[ x ]] ρ = ρ x
Eval [[ E1 E2 ]] ρ = (Eval [[ E1 ]] ρ) (Eval [[ E2 ]] ρ)
Eval [[ λx.E ]] ρ a = Eval [[ E ]] ρ[x = a]
kde k je konštanta alebo operátor
x je premenná
a je hodnota
E, E1, E2 sú výrazy
ρ je prostredie, t.j. funkcia zobrazujúca mená
premenných do ich hodnôt
Platí ρ[x = a] x = a
ρ[x = a] y = ρ y
kde ρ[x = a] je funkcia ρ, ktorá má premennú x viazanú na hodnotu a.
Definícia 10: Symbol ⊥Symbol ⊥ označuje nedefinovanú hodnotu a je definovaný nasledovne:
Eval [[ E ]] = ⊥
kde E je výraz, ktorý nemá normálnu formu.
Denotačná sémantika jazyka lambda
Ján Kollár Funkcionálne programovanie 58
Definícia 11: Striktná funkcia
Funkcia f je striktná vzhľadom na svoj argument práve vtedy, ak platí:
f ⊥ = ⊥
Definícia 12: Nestriktná funkcia
Funkcia f je nestriktná vzhľadom na svoj argument práve vtedy, ak platí:
f ⊥ = a
Striktnosť a nestriktnosť funkcie – definícia
Ján Kollár Funkcionálne programovanie 59
Eval [[ ∨ ]] True True = TrueEval [[ ∨ ]] True False = TrueEval [[ ∨ ]] False True = TrueEval [[ ∨ ]] False False = FalseEval [[ ∨ ]] True ⊥ = TrueEval [[ ∨ ]] ⊥ True = TrueEval [[ ∨ ]] False ⊥ = ⊥Eval [[ ∨ ]] ⊥ False = ⊥Eval [[ ∨ ]] ⊥ ⊥ = ⊥
Eval [[ IF ]] True a b = a
Eval [[ IF ]] False a b = b
Eval [[ IF ]] ⊥ a b = ⊥
Eval [[ < ]] x y = x < y , ak x 6= ⊥ a y 6= ⊥Eval [[ < ]] ⊥ y = ⊥Eval [[ < ]] x ⊥ = ⊥
Eval [[ / ]] x y = x/y , ak x 6= ⊥ a y 6= ⊥ a y 6= 0Eval [[ / ]] x y = ⊥ , ak x 6= ⊥ a y 6= ⊥ a y = 0Eval [[ < ]] ⊥ y = ⊥Eval [[ < ]] x ⊥ = ⊥
Striktnosť a nestriktnosť funkcie – príklady
Ján Kollár Funkcionálne programovanie 60
Všeobecne platí:
Eval [[ k ]] = k
kde k na ľavej strane je konštanta ako syntaktický objekt a k na pravej strane je hodnota konštanty (čísla, operátora alebo
konštruktora). Napríklad:
Eval [[ 3 ]] = 3 ; numerická konštanta
Eval [[ ′a′ ]] = ′a′ ; znaková konštanta
Eval [[ IF ]] = IF ; operátor
Eval [[ + ]] = + ; operátor
Eval [[ True ]] = True ; konštruktor
Eval [[ Nil ]] = Nil ; konštruktor
Eval [[ Cons ]] = Cons ; konštruktor
Sémantika konštanty a operátora
Ján Kollár Funkcionálne programovanie 61
Definícia 13: Výpočet na základe hodnoty
Základným princípom tohto výpočtu je mechanizmus, pri ktorom hodnoty argumentov funkcie musia byť vypočítané predtým,
ako je funkcia aplikovaná. Ide o princíp vnútornej redukcie. Odovzdávanie skutočných parametrov je realizované hodnotou (call-
by-value).
Definícia 14: Výpočet na základe požiadavky
Pre tento výpočet je charakteristické, že hodnoty argumentov funkcie sú vypočítané až na základe požiadavky. Ide teda o princíp
vonkajšej redukcie. Odovzdávanie skutočných parametrov je realizované menom (call-by-name).
Nestriktnosť je matematickou vlastnosťou funkcie (operácie) a výpočet na základe požiadavky je jej výhodným sekvenčným
implementačným mechanizmom, pretože neužitočný výraz netreba vôbec počítať.
Princíp výpočtu na základe požiadavky a hodnoty
Ján Kollár Funkcionálne programovanie 62
Definícia 15: Superkombinátor
Superkombinátor $S násobnosti n je výraz lambda v tvare:
$S = λx1.λx2. . . . λxn. E
kde E nie je abstrakcia lambda, taký, že platí
1. $S neobsahuje voľné premenné
2. Každá abstrakcia lambda vo výraze E je superkombinátorom
3. n ≥ 0
Kombinátor je špecif ickým výrazom lambda a superkombinátor je špecif ickým kombinátorom.
Transformácia do superkombinátorovej formy znamená odstránenie voľných premenných. Algoritmus, pomocou ktorého je to
možné dosiahnuť, sa nazýva λ− lifting.
Kombinátory – superkombinátory
Ján Kollár Funkcionálne programovanie 63
Pravidlá pre transformáciu abstrakcií lambda do kombinátorov SK sa nazývajú transformácie S, K a I, a sú nasledovné:
(λx.e1 e2) ⇒ S (λx.e1) (λx. e2)(λx.c) ⇒ K c (c 6= x)(λx.x) ⇒ I
Napríklad nech funkcia h je vyjadrená nasledovnou abstrakciou lambda:
h = λx. OR x TRUE
kde OR a TRUE sú konštanty. Dvojnásobným použitím transformácie S dostaneme:
(λx. OR x TRUE)S⇒ S (λx. OR x) (λx. TRUE)S⇒ S (S (λx. OR) (λx. x)) (λx. TRUE)
Ďalej použitím transformácie I a K dostaneme výsledok nasledovne:
((S (λx. OR) (λx. x)) (λx. TRUE))I⇒ S (S (λx. OR) I) (λx. TRUE)K⇒ S (S (K OR) I) (λx. TRUE)K⇒ S (S (K OR) I) (K TRUE)
Funkciu h teda možno vyjadriť v tvare kombinátorov S, K a I nasledovne:
h = S (S (K OR) I) (K TRUE)
Kombinátory SK – transformácia výrazu lambda
Ján Kollár Funkcionálne programovanie 64
Redukčné pravidlá pre kombinátory S, K, a I sú nasledovné:
S f g x → f x (g x)K c y → cI x → x
Príklad redukcie výrazu (h y):
(h y)
= S (S (K OR) I) (K TRUE) yS→ S (K OR) I y (K TRUE y)S→ K OR y (I y) (K TRUE y)K→ OR (I y) (K TRUE y)I→ OR y (K TRUE y)K→ OR y TRUE
čo skutočne zodpovedá výrazu
(λx. OR x TRUE) y
po redukcii beta.
Nazáver možno ukázať, že kombinátor I možno nahradiť výrazom (S K K) a teda kombinátor I je nadbytočný. Ak aplikujeme
výraz (S K K) na výraz x dostaneme výraz x:
(S K K) xS→ K x (K x)K→ x
čo zodpovedá aplikácii I na x:I xI→ x
Výraz (S K K) je teda ekvivalentný kombinátoru I.
Kombinátory SK – redukcia výrazu v tvare kombinátorov SK
FUNKCIONÁLNE PROGRAMOVANIE
c© prof. Ing. Ján Kollár, CSc.Katedra počítačov a informatiky
Fakulta elektrotechniky a informatiky
Technická univerzita v Košiciach
2016
65