65
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

¨istØ funkcionÆlne jazyky Teoretický zÆklad ...people.tuke.sk/jan.kollar/fp/fp.pdfJÆn KollÆr FunkcionÆlne programovanie 1 Jazyky procedurÆlne, objektovØ, deklaratívne (aplikatívne,

  • 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