Induktivni tipi#
Definirajmo sledeče tri funkcije:
let rec (@) xs ys =
match xs with
| [] -> ys
| x :: xs' -> x :: (xs' @ ys)
val ( @ ) : 'a list -> 'a list -> 'a list = <fun>
let rec obrni = function
| [] -> []
| x :: xs -> obrni xs @ [x]
val obrni : 'a list -> 'a list = <fun>
let rec dolzina = function
| [] -> 0
| _ :: xs -> 1 + dolzina xs
val dolzina : 'a list -> int = <fun>
Iz definicij takoj sledijo enačbe
[] @ ys = ys(x :: xs) @ ys = x :: (xs @ ys)obrni [] = []obrni (x :: xs) = obrni xs @ [x]dolzina [] = 0dolzina (x :: xs) = 1 + dolzina xs
Iz teh enačb na primer takoj sledi trditev obrni [x] = [x].
obrni [x]
= obrni (x :: [])
(ker je `[x]` okrajšava za `x :: []`)
= [] @ [x]
(po (4))
= [x]
(po (1))
Indukcija na seznamih#
Trditev: dolzina (xs @ ys) = dolzina xs + dolzina ys#
Tukaj enostavno odvijanje definicij ne bo pomagalo, saj izraz dolzina (xs @ ys) ne ustreza ne levi ne desni strani nobene od zgoraj naštetih definicij. Namesto tega uporabimo načelo indukcije za sezname:
P([]) ∧ (∀ z, zs. P(zs) ⇒ P(z :: zs)) ⟹ ∀ ws. P(ws)
Torej, lastnost P velja za vse sezname ws, kadar (1) velja za prazen seznam [] in (2) velja za sestavljen seznam z :: zs ob predpostavki, da velja za rep zs. Načelo indukcije je podobno načelu indukcije za naravna števila:
P(0) ∧ (∀ m. P(m) ⇒ P(m⁺)) ⟹ ∀ n. P(n)
Torej, lastnost P velja za vsa naravna števila n, kadar (1) velja za 0 in (2) velja za naslednika m⁺ ob predpostavki, da velja za m. Izjavo dokažemo z indukcijo na levi seznam. Indukcija poteka v dveh korakih:
Osnovni korak#
V osnovnem koraku pokažemo, da enakost velja, kadar je levi seznam prazen, torej oblike []:
dolzina ([] @ ys)
= dolzina ys
(po (1))
= 0 + dolzina ys
(po Peanovih aksiomih)
= dolzina [] + dolzina ys
(po (5))
Indukcijski korak#
V indukcijskem koraku pokažemo, da enakost velja za sestavljeni seznam x :: xs ob predpostavki, da enakost velja za seznam xs:
dolzina ((x :: xs) @ ys)
= dolzina (x :: (xs @ ys))
(po (2))
= 1 + dolzina (xs @ ys)
(po (6))
= 1 + (dolzina xs + dolzina ys)
(po indukcijski prepostavki)
= (1 + dolzina xs) + dolzina ys)
(po Peanovih aksiomih)
= dolzina (x :: xs) + dolzina ys
(po (6))
Pravilnost načela indukcije na seznamih#
Zakaj sploh smemo uporabiti načelo indukcije? Recimo, da velja
P([]) ∧ (∀ z, zs. P(zs) ⇒ P(z :: zs))
Zakaj potem velja P(ws) za poljuben seznam ws? Vzemimo nek konkreten seznam, na primer [1; 2; 3], in pokažimo, da velja P([1; 2; 3]). Res, po prvem delu predpostavke velja P([]). Zato po drugem delu predpostavke velja P([3]), saj velja P([]) in je [3] = 3 :: []. Podobno potem iz P([3]) sledi P([2; 3]) in na koncu tudi P([1; 2; 3]). Tak dokaz bi lahko ponovili za vsak konkreten seznam.
V splošnem pa se naslonimo na načelo indukcije za naravna števila. Definirajmo:
Q(n) := ∀ ws. |ws| = n ⇒ P(ws)
Torej, Q(n) velja, če velja P(ws) za vse sezname ws dolžine n. Iz Peanovih aksiomov potem sledi:
Q(0) ∧ (∀m. Q(m) ⇒ Q(m+)) ⟹ ∀n. Q(n)
Pokažimo, da velja Q(0) in ∀m. Q(m) ⇒ Q(m+). Ker velja P([]) in je [] edini seznam dolžine 0, velja tudi Q(0). Vzemimo poljuben m in predpostavimo Q(m). Torej za poljuben seznam zs dolžine m velja P(zs). Iz predpostavke potem sledi, da velja tudi P(z :: zs). Ker so vsi seznami dolžine m⁺ take oblike, smo pokazali Q(m⁺). Po indukciji na naravnih številih torej sledi ∀n. Q(n). Ker je vsak seznam ws končen, ima za dolžino neko naravno število, zato velja P(ws) za vse sezname ws.
Trditev: xs @ [] = xs#
Po (1) vemo, da je [] @ xs = xs, torej je [] leva enota za @. To, da je [] tudi desna enota, pa ni samoumevno — za dokaz uporabimo indukcijo.
Osnovni korak#
Po (1) velja [] @ [] = [], kar dokaže osnovni korak.
Indukcijski korak#
Prepostavimo, da velja xs @ [] = xs. Tedaj velja
(x :: xs) @ []
= x :: (xs @ [])
(po (2))
= x :: xs
(po indukcijski prepostavki)
kar zaključi tudi indukcijski korak.
Trditev xs @ (ys @ zs) = (xs @ ys) @ zs#
Operacija stikanja seznamov @ je tudi asociativna, kar dokažemo z indukcijo na xs. Če zapišete dokaz asociativnosti seštevanja, lahko vidite, da poteka podobno, le da se namesto [] pojavlja 0, namesto x :: xs pa naslednik n⁺.
Osnovni korak#
[] @ (ys @ zs)
= ys @ zs
(po (1))
= ([] @ ys) @ zs
(po (1))
Indukcijski korak#
Prepostavimo, da velja xs @ (ys @ zs) = (xs @ ys) @ zs. Tedaj velja
(x :: xs) @ (ys @ zs)
= x :: (xs @ (ys @ zs))
(po (2))
= x :: ((xs @ ys) @ zs)
(po indukcijski predpostavki)
= (x :: (xs @ ys)) @ zs
(po (2))
= ((x :: xs) @ ys) @ zs
(po (2))
Trditev obrni (xs @ ys) = obrni ys @ obrni xs#
Osnovni korak#
obrni ([] @ ys)
= obrni ys
(po (1))
= obrni ys @ []
(po prej dokazani trditvi ∀ xs. xs @ [] = xs)
= obrni ys @ obrni []
(po (3))
Indukcijski korak#
obrni ((x :: xs) @ ys)
= obrni (x :: (xs @ ys))
(po (2))
= obrni (xs @ ys) @ [x]
(po (4))
= (obrni ys @ obrni xs) @ [x]
(po indukcijski predpostavki)
= obrni ys @ (obrni xs @ [x])
(po prej dokazani trditvi ∀ xs, ys, zs. xs @ (ys @ zs) = (xs @ ys) @ zs)
= obrni ys @ obrni (x :: xs)
(po (4))
Trditev dolzina (obrni xs) = dolzina xs#
Osnovni korak#
Iz definicije (3) sledi dolzina (obrni []) = dolzina [],
Indukcijski korak#
Za indukcijski korak moramo pokazati, da velja dolzina (obrni (x :: xs)) = dolzina (x :: xs) ob indukcijski predpostavki dolzina (obrni xs) = dolzina xs.
Velja:
dolzina (obrni (x :: xs))
= dolzina (obrni xs @ [x])
(po definiciji (4))
= dolzina (obrni xs) + dolzina [x]
(po zgornji lemi)
= dolzina xs + dolzina [x]
(po indukcijski predpostavki)
= dolzina xs + 1
(po definicijah (5) in (6))
= 1 + dolzina xs
(zaradi komutativnosti seštevanja)
= dolzina (x :: xs)
(po definiciji (6))
s čimer zaključimo tudi indukcijski korak.
Indukcija na drevesih#
Načela indukcije imamo na voljo tudi na drugih vsotah. Na primer, če definiramo tip dvojiških dreves:
type 'a drevo =
| Prazno
| Sest of 'a drevo * 'a * 'a drevo
zanj velja načelo indukcije:
P(Prazno) ∧ (∀ l, x, d. P(l) ∧ P(d) ⇒ P(Sest (l, x, d))) ⟹ ∀ t. P(t)
Torej, lastnost P velja za vsa drevesa t, kadar (1) velja za prazno drevo Prazno in (2) velja za sestavljeno drevo Sest (l, x, d) ob predpostavki, da velja za otroka l in d.
Definirajmo funkciji:
let rec zrcali = function
| Prazno -> Prazno
| Sest (l, x, d) -> Sest (zrcali d, x, zrcali l)
let rec globina = function
| Prazno -> 0
| Sest (l, _, d) -> 1 + max (globina l) (globina d)
Iz definicij sledijo enačbe
zrcali Prazno = Praznozrcali (Sest (l, x, d)) = Sest (zrcali d, x, zrcali l)globina Prazno = 0globina (Sest (l, x, d)) = 1 + max (globina l) (globina d)
Trditev globina (zrcali t) = globina t#
Osnovni korak#
globina (zrcali Prazno)
= globina Prazno
(po (1))
Indukcijski korak#
globina (zrcali (Sest (l, x, d)))
= globina (Sest (zrcali d, x, zrcali l))
(po (2))
= 1 + max (globina (zrcali d)) (globina (zrcali l))
(po (4))
= 1 + max (globina d) (globina (zrcali l))
(po prvi indukcijski predpostavki)
= 1 + max (globina d) (globina l)
(po drugi indukcijski predpostavki)
= 1 + max (globina l) (globina d)
(zaradi komutativnosti max)
= globina (Sest (l, x, d))
(po (4))
Vaje za utrjevanje#
Trditev obrni (obrni xs) = xs#
Trditev zrcali (zrcali t) = t#
Trditev obrni xs = obrni' xs (težja)#
Kot vemo, ima funkcija obrni, definirana kot:
let rec obrni = function
| [] -> []
| x :: xs -> obrni xs @ [x]
časovno zahtevnost \(O(n^2)\), saj se mora zapeljati čez ves seznam, da mu na konec doda x.
Bolje je, če uporabimo funkcijo obrni', ki uporablja akumulator in je definirana kot:
let obrni' =
let rec aux acc = function
| [] -> acc
| x :: xs -> aux (x :: acc) xs
in
aux []
Pokažite, da za vse sezname xs velja obrni xs = obrni' xs.
Indukcija na drugih tipih#
Definirajmo tipe
type naravno = Zero | Succ of naravno
type boole = Resnica | Laz
type 'a mogoce = Nic | Nekaj of 'a
Kakšni sta videti načeli indukcije za te tipe?