150 likes | 348 Views
Mer om Nr 7. Rekursion, listor, typer och funktioner. Mönstermatchning på listor av tuppler. fun zip [] [] = [] | zip (x::xs) (y::ys) = (x,y) :: zip xs ys | zip _ _ = raise Zip local fun unzip1 (f,s) [] = (f,s) | unzip1 (f,s) ((x,y)::xys) = unzip1 (x::f,y::s) xys
E N D
Mer omNr 7 Rekursion, listor, typer och funktioner
Mönstermatchning på listor av tuppler fun zip [] [] = [] | zip (x::xs) (y::ys) = (x,y) :: zip xs ys | zip _ _ = raise Zip local fun unzip1 (f,s) [] = (f,s) | unzip1 (f,s) ((x,y)::xys) = unzip1 (x::f,y::s) xys in val unzip = unzip1 (nil,nil) end • Behålla ordningen på elementen i listan fun unzip [] = (nil,nil) | unzip ((x,y)::xys) = letval (f,s) = unzip xys in (x::f,y::s) end
Typuttryck • Hur beskrivs en typ dvs hur ser typuttryck ut: Typvariabler: 'a, 'b, … Eqtypvariabler: ''a, ''b, ... Konstanta typer: int, bool, real, string, unit Tuppler: type1 * … * typen Funktioner: type1 -> … -> typen Typfunktioner: type typefunct, type list Flera argument: (type1,…,typen) typefunct
Namngivna typer • Motsvarigheten till namngivna värden är namngivna typer. • Namnet har då exakt samma betydelse som den namngivna typen och de är utbytbara. • Varför namnge typer? • Illustrativa namn. Vi kan använda ett namn som säger vad vi avser att värden av den typen ska betyda. • Kortare uttryck. En långt komplicerat typuttryck kan ges ett kortare namn
Exempel • Istället för en tuppel med olika typer. (string * bool * (int * int)) list • ger vi illustrativa namn åt de olika delarna type regnum = string type enter_or_exit = bool type hour = int type minute = int type time = hour * minute type entry = regnum * enter_or_exit * time type p_house = entry list fun report (ph : p_house) = ... val report = fn : p_house -> …
Polymorfa typuttryck type 'a pair = 'a * 'a fun f ((x,y): 'a pair) = x f (3,4) => 3 f (4.0,3.0) => 4.0 f (true, 5) => TypeClash
Rekursionsformer • Stack rekursiva funktioner • Varje anrop måste vänta in resultat av nästa rekursiva anrop, beräkningarna läggs på en stack. • Ackumulerande rekursion • Ett ackumulerande argument används vid beräkningarna.
Svansrekursion • Evalueringen av en funktionskropp behöver aldrig uppskjutas för att vänta på resultatet från nästa rekursiva anrop. • Resultatet av en funktion är det direkta resultatet av ett rekursivt anrop. exception Last; fun last [ ] = raise Last | last [x] = x | last (x::xs) = last xs; val last = fn : 'a list -> 'a • Ackumulerande rekursion är ofta (ej alltid) svansrekursiv.
Ömsesidig rekursion • Flera funktioner som anropar varandra. Alla funktionerna måste definieras "samtidigt". fun even 0 = true | even n = odd (n-1) and odd 0 = false | odd n = even (n-1); > val even = fn:int -> bool and odd = fn:int -> bool
Flera rekursionsanrop • Linjärrekursion: • Ett anrop ger upphov till ett nytt anrop. • Dubbel rekursion • Ett anrop ger upphov till två nya anrop fun fib 0 = 0 | fib 1 = 1 | fib n = fib (n-1) + fib (n-2); > val fib = fn : int -> int map fib (fromto 0 10); > [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55] : int list
Quicksort • Välj ett element (första) dela upp listan element som är större än det valda och de som är mindre. Sortera de delarna och lägg sedan ihop dem. fun quick [] = [] | quick [x:int] = [x] | quick (x::xs) = letval small = filter (fn y => y <= x) xs val large = filter (fn y => y > x) xs in quick small @ [x] @ quick large end val quick = fn : int list -> int list
Nästlad rekursion • En rekursiv funktionsapplikation "inuti" en annan. • Ackermann's funktion fun ack 0 m = m+1 | ack n 0 = ack (n-1) 1 | ack n m = ack (n-1) (ack n (m-1)); val ack = fn : int -> int -> int ack 3 3; > val it = 61 : int ack 3 4; > val it = 125 : int ack 3 5; > val it = 253 : int ack 4 1; > val it = 65533 : int
Curried och uncurried • En curried funktion är partiellt applicerbara. • Dvs en funktion som kan ta "ett argument i taget". Tar ett argument och returnerar en ny funktion som tar nästa argument osv. • Curried - fun add x y :int = x+y; > val add = fn: int -> (int -> int); • Uncurried - fun plus (x,y):int = x+y; > val plus = fn:(int * int) -> int;
Omvandling • Det är alltid möjligt att göra om en ”uncurried” funktion till ”curried” och vice versa. fun curry ff x y = ff (x,y); > val curry = fn: (’a * ’b -> ’c) -> ’a -> ’b -> ’c curry plus; > val it = fn: int -> int -> int curry plus 2 3; > val it = 5:int fun uncurry f (x,y) = f x y > valuncurry=fn:('a->'b->'c)->('a*'b)->'c uncurry add; > val it = fn : (int * int) -> int uncurry add (2,3); > val it = 5 : int
Operatorer uncurried • ML:s operatorer är infixa, ”uncurried”. • op gör en infix operator prefix op / ; val it = fn : real * real -> real op + (2,3); val it = 5 : int curry op+ 2 3; val it = 5 : int