TIEA341 Funktio-ohjelmointi 1, kevät 2008 Luento 10 Todistamisesta Antti-Juhani Kaijanaho Jyväskylän yliopisto Tietotekniikan laitos 21. tammikuuta 2008
Samuuden todistaminen usein onnistuu ihan laskemalla (kuten kurssin alussa tehtiin) ei kuitenkaan aina esim. miten todistaa, että kaikille äärellisille listoille xs pätee foldr (:) [] xs = xs?
Rakenneinduktio listojen tapauksessa Tarkasteltavana väite P, joka on parametrisoitu listan suhteen: 1. Jos P([]) on tosi,... 2.... ja jos P(x : xs) on tosi kaikilla x aina kun P(xs) on tosi,... 3.... niin P(xs) on tosi kaikilla äärellisillä listoilla xs. Todistus tällä tekniikalla on todistus listan xs rakenteen suhteen. Askel 1 on perustapaus. Askel 2 on induktioaskel, ja siinä P(xs) on induktio-oletus.
Väite: foldr (:) [] xs = xs kaikilla äärellisillä xs Muista: f o l d r f z [ ] = z (D1) f o l d r f z ( x : x s ) = f x ( f o l d r f z x s ) (D2) Todistus induktiolla xs:n rakenteen suhteen: Perustapaus Induktiotapaus foldr (:) [] [] = [] (D1) foldr (:) [] (x : xs) = (:) x (foldr (:) [] xs) (D2) = (:) x xs induktio-oletus = x : xs syntaksi
Väite: length (xs ++ ys) = length xs + length ys kaikilla äärellisillä xs ja ys l e n g t h [ ] = 0 ( L1 ) l e n g t h (_: l ) = 1 + l e n g t h l ( L2 ) [ ] ++ y s = y s ( C1 ) ( x : x s ) ++ y s = x : ( x s ++ y s ) ( C2 ) Todistus induktiolla xs:n rakenteen suhteen, kun ys on mielivaltainen äärellinen lista: Perustapaus Induktioaskel... length ([] ++ ys) = length ys (C1) = 0 + length ys aritm. = length [] + length ys (L1)
Väite: length (xs ++ ys) = length xs + length ys kaikilla äärellisillä xs ja ys l e n g t h [ ] = 0 ( L1 ) l e n g t h (_: l ) = 1 + l e n g t h l ( L2 ) [ ] ++ y s = y s ( C1 ) ( x : x s ) ++ y s = x : ( x s ++ y s ) ( C2 ) Todistus induktiolla xs:n rakenteen suhteen, kun ys on mielivaltainen äärellinen lista: Perustapaus... Induktioaskel length ((x : xs) ++ ys) = length (x : (xs ++ ys)) (C2) = 1 + length (xs ++ ys) (L2) = 1 + (length xs + length ys) induktio-oletus = (1 + length xs) + length ys aritm. = length (x : xs) + length ys (L2)
Väite: reverse xs = foldl (flip (:)) [] xs kaikilla äärellisillä listoilla xs r e v e r s e [ ] = [ ] ( R1 ) r e v e r s e ( x : x s ) = r e v e r s e x s ++ [ x ] ( R2 ) f o l d l f z [ ] = z ( F1 ) f o l d l f z ( x : x s ) = f o l d l f ( f z x ) x s ( F2 ) f l i p f x y = f y x ( FL ) [ ] ++ y s = y s ( C1 ) ( x : x s ) ++ y s = x : ( x s ++ y s ) ( C2 ) Todistus induktiolla xs:n rakenteen suhteen: Perustapaus: Induktioaskel:... reverse [] = [] (R1) = foldl (flip (:)) [] [] (F 1)
Väite: reverse xs = foldl (flip (:)) [] xs kaikilla äärellisillä listoilla xs r e v e r s e [ ] = [ ] ( R1 ) r e v e r s e ( x : x s ) = r e v e r s e x s ++ [ x ] ( R2 ) f o l d l f z [ ] = z ( F1 ) f o l d l f z ( x : x s ) = f o l d l f ( f z x ) x s ( F2 ) f l i p f x y = f y x ( FL ) [ ] ++ y s = y s ( C1 ) ( x : x s ) ++ y s = x : ( x s ++ y s ) ( C2 ) Todistus induktiolla xs:n rakenteen suhteen: Perustapaus:... Induktioaskel:... tarvitaan lemma reverse (x : xs) = reversexs ++[x] (R2) = foldl (flip (:)) [] xs ++[x] induktio-oletus = foldl (flip (:)) [] (x : xs)??? foldl (flip (:)) [] xs ++[x] = foldl (flip (:)) [] (x : xs)
Lemma: foldl (flip (:)) [] xs ++[x] = foldl (flip (:)) [] (x : xs) f o l d l f z [ ] = z ( F1 ) f o l d l f z ( x : x s ) = f o l d l f ( f z x ) x s ( F2 ) f l i p f x y = f y x ( FL ) [ ] ++ y s = y s ( C1 ) ( x : x s ) ++ y s = x : ( x s ++ y s ) ( C2 ) Todistus laskemalla: foldl (flip (:)) [] xs ++[x] = foldl (flip (:)) ([] ++[x]) xs??? = foldl (flip (:)) [x] xs (C1) = foldl (flip (:)) (flip (:) [] x) xs (FL) = foldl (flip (:)) [] (x : xs) (F 2) Ensimmäisessä askeleessa tarvitaan uutta lemmaa: foldl (flip (:)) ys xs ++[y] = foldl (flip (:)) (ys ++[y]) xs kaikilla äärellisillä listoilla xs ja ys sekä kaikilla y.
Lemma: foldl (flip (:)) ys xs ++[y] = foldl (flip (:)) (ys ++[y]) xs f o l d l f z [ ] = z ( F1 ) f o l d l f z ( x : x s ) = f o l d l f ( f z x ) x s ( F2 ) f l i p f x y = f y x ( FL ) [ ] ++ y s = y s ( C1 ) ( x : x s ) ++ y s = x : ( x s ++ y s ) ( C2 ) Induktio xs:n rakenteen suhteen: Perustapaus: Induktioaskel:... foldl (flip (:)) ys [] ++[y] = ys ++[y] (F 1) = foldl (flip (:) (ys ++[y]) [] (F 1)
Lemma: foldl (flip (:)) ys xs ++[y] = foldl (flip (:)) (ys ++[y]) xs f o l d l f z [ ] = z ( F1 ) f o l d l f z ( x : x s ) = f o l d l f ( f z x ) x s ( F2 ) f l i p f x y = f y x ( FL ) [ ] ++ y s = y s ( C1 ) ( x : x s ) ++ y s = x : ( x s ++ y s ) ( C2 ) Induktio xs:n rakenteen suhteen: Perustapaus:... Induktioaskel: foldl (flip (:)) ys (x : xs) ++[y] = foldl (flip (:)) (flip (:) ys x) xs ++[y] (F 2) = foldl (flip (:)) (x : ys) xs ++[y] (FL) = foldl (flip (:)) ((x : ys) ++[y]) xs induktio-oletus = foldl (flip (:)) (x : (ys ++[y])) xs (C2) = foldl (flip (:)) (flip (:) (ys ++[y]) x) xs (FL) = foldl (flip (:)) (ys ++[y]) (x : xs) (F 2)
map n ominaisuuksia Seuraavat pätevät kaikilla funktioilla f ja g sekä kaikilla äärellisillä listoilla xs ja ys, kunhan tyypit menevät oikein: map (\x x) = \x x map (f. g) = map f. map g map f. tail = tail. map f map f. reverse = reverse. map f map f. concat = concat. map f map f (xs ++ ys) = map f xs ++ map f ys f. head = head. map f jos f on strikti 1 1 Määritellään myöhemmin.
foldien ominaisuuksia Seuraavat pätevät kaikilla operaattoreilla ja ja kaikilla e, kunhan tyypit menevät oikein: 1. Jos (x y) z = x (y z) ja e x = x ja x e = x pätevät kaikilla x, y ja z, niin kaikilla äärellisillä listoilla xs pätee foldr ( ) e xs = foldl ( ) e xs 2. Jos (x y) z = x (y z) ja x e = e x pätevät kaikilla x, y ja z, niin kaikilla äärellisillä listoilla xs pätee foldr ( ) e xs = foldl ( ) e xs 3. Kaikilla äärellisillä listoilla xs pätee foldr ( ) e xs = foldl (flip ( )) e (reverse xs)
Muita ominaisuuksia Kaikilla äärellisillä xs ja ys sekä kaikilla ei-negatiivisilla n ja m pätee: (xs ++ ys) ++ zs = xs ++(ys ++ zs) xs ++[] = [] take n xs ++ drop n xs = xs take m. take n = take (min m n) drop m. drop n = drop (m + n) take m. drop n = drop n. take (m + n) drop m. take n = take (n m). drop m reverse (reverse xs) = xs head (reverse xs) = last xs last (reverse xs) = head xs jos n m
Striktiys Otetaan käyttöön merkintätapa: jos lausekken laskenta ei koskaan pääty tai se päättyy virheeseen, lausekkeen arvoksi merkitään (pohja). Vastaavasti :ia voidaan käyttää merkitsemään lauseketta, jonka laskenta ei pääty tai jonka laskenta päättyy virheeseen. Määritellään, että funktio f on strikti, jos f = pätee ja nonstrikti, jos f pätee.
Esimerkkejä (+1) on strikti funktio. (+) on strikti funktio molempien parametriensa suhteen. id on nonstrikti funktio.
Striktiys Haskellissa Haskell-funktiot ovat aina lähtökohtaisesti nonstriktejä. Haskell-funktio on strikti jonkin parametrinsa suhteen, jos: 1. kyseistä parametria sovitetaan koettelevaan (engl. refutable) hahmoon, tai 2. kyseinen parametri annetaan jollekin striktille funktiolle argumenttina. Kaikki muut hahmot ovat koettelevia paitsi muuttujat, jokerit (_) ja ~-hahmot.
Esimerkki True && x = x False && _ = False (&&) on ensimmäisen parametrinsa suhteen strikti, koska ko. parametria sovitetaan koettelevaan hahmoon True. (&&) on toisen parametrinsa suhteen nonstrikti, koska sitä ei soviteta koettelevaan hahmoon eikä sitä myöskään anneta millekään striktille funktiolle argumentiksi.
Ekstensioperiaate Kaksi funktiota f ja g ovat samat, jos f x = g x pätee kaikilla x.
Väite: f. head = head. map f jos f on strikti head ( x :_) = x (HE) f. g = \ x > f ( g x ) (CO) map f [ ] = [ ] (M1) map f ( x : x s ) = f x : map f x s (M2) Ekstensioperiaatteen perusteella induktio parametrilistan rakenteen suhteen: Perustapaus: (f. head) [] Induktioaskel:... = (\x f (head x)) [] (CO) = f (head []) funktion soveltaminen = f (HE) ja epäonninen sovitus = oletus: f on strikti = head [] (HE) ja epäonninen sovitus = head (map f []) (M1) = (\x head (map f x)) [] funktion abstrahoiminen = (head. map f ) [] (CO)
Väite: f. head = head. map f jos f on strikti head ( x :_) = x (HE) f. g = \ x > f ( g x ) (CO) map f [ ] = [ ] (M1) map f ( x : x s ) = f x : map f x s (M2) Ekstensioperiaatteen perusteella induktio parametrilistan rakenteen suhteen: Perustapaus:... Induktioaskel: (f. head) (x : xs) = (\y f (head y)) (x : xs) (CO) = f (head (x : xs)) funktion soveltaminen = f x (HE) = head (f x : map f xs) (HE) = head (map f (x : xs)) (M2) = (\y head (map f y)) (x : xs) funktion abstrahoiminen = (head. map f ) (x : xs) (CO) Entä äärettömät parametrilistat?
Rakenneinduktio äärettömillä listoilla Tarkasteltavana yhtälö E 1 (xs) = E 2 (xs), joka on parametrisoitu listan xs suhteen: 1. Jos E 1 ( ) = E 2 ( ) on tosi,... 2.... ja jos E 1 (x : xs) = E 2 (x : xs) on tosi kaikilla x aina kun E 1 (xs) = E 2 (xs) on tosi,... 3.... niin E 1 (xs) = E 2 (xs) on tosi kaikilla äärettömillä listoilla xs. Todistus tällä tekniikalla on todistus äärettömän listan xs rakenteen suhteen. Askel 1 on pohjatapaus. Askel 2 on induktioaskel, ja siinä E 1 (xs) = E 2 on induktio-oletus. Jos lisäksi todistetaan perustapaus E 1 ([]) = E 2 ([]), niin yhtälö on tosi kaikilla listoilla (niin äärellisillä kuin äärettömillä).
Väite: f. head = head. map f jos f on strikti head ( x :_) = x (HE) f. g = \ x > f ( g x ) (CO) map f [ ] = [ ] (M1) map f ( x : x s ) = f x : map f x s (M2) Ekstensioperiaatteen perusteella induktio parametrilistan rakenteen suhteen: Pohjatapaus: (f. head) = (\x f (head x)) (CO) = f (head ) funktion soveltaminen = f (HE), koetteleva hahmonsovitus = Oletus: f on strikti = head (HE), koetteleva hahmonsovitus = head (map f ) (M1), koetteleva hahmonsovitus = (\x head (map f x)) funktion abstrahoiminen = (head. map f ) (CO) Perustapaus:... Induktioaskel:...
Rakenneinduktio muilla Haskellin rekursiivisilla tyypeillä käydään läpi kaikki tyypin koostimet yksi kerrallaan rekursiivisten koostimien kohdalla saadaan käyttää induktio-oletusta jos halutaan, voidaan käydä läpi pohjatapauskin, jolloin todistus toimii äärettömilläkin