F6: Högre ordningens funktioner Mönster för rekursion (1) Mönster för rekursion Partiellt applicerbara funktioner Anonyma funktioner Op HOF på listor Sortering Listreduktion Funktionskomposition Rekursivt anrop i lokal deklaration. Speciellt användbart när resultatet av det rekursiva anropet ska "bearbetas" (ex delas upp) på nåt sätt. Ex: Dela upp en heltalslista i två listor, tal <0 och >=0 fun split [] = ([],[]) split (x::xs) = let val (small,large) = split xs in if x<0 then (x::small,large) else (small,x::large) 1 2 Mönster för rekursion (1b) Mönster för rekursion (2) Det rekursiva anropet av huvudfunktionen använder resultatet av bearbetning av första elementet. fun f(y,x::xs) = let val (a,b)=help(x,y) in f(y+a, (tl xs)@[b]) Rekursivt anrop i "huvudfunktionskroppen". T ex när bearbetningen av de olika listelementen är (nästan) oberoende av varann. Ex: Givet en lista på studenter (string list), returnera en lista av tripler (pnr, namn, #poäng). Funktionerna get_kurslist, get_pnr och sumpoints finns givna. fun studlist [] = [] studlist (name::rest)= let val kurslista = get_kurslist(name) val pnr = get_pnr(name) in (pnr, name, sumpoints(kurslista)):: studlist rest 3 4 Mönster för rekursion (2b) Högre Ordningens Funktioner En hjälpfunktion som returnerar ett sammansatt resultat till huvudfunktionen local fun help x= let val a= val b= val c= in (a,b,c) in fun main (y::ys) = let (a1,b1,c1)= help y in a1 b1 c1 main ys end Högre ordningens funktioner? Funktioner som kan ta funktioner som argument Funktioner som kan ge en funktion som resultat Partiellt applicerabara funktioner Funktionen som fullvärdig medlem i språket 5 6
Partiellt applicerbara funktioner Partiellt applicerbara funktioner Ex: skatteberäkning. Min skatt i kronor beror på a) skatteprocenten; b) min lön fun tax1 tax = (*En funktion som tar en skattesats och returnerar en ny funktion som tar min lön och beräknar skatten. En skattetabell *); fun tax2 loen = (tax1 UmeaSkatt) loen; Precis så kan man resonera i ML, men kombinera tax1 och tax2 i en funktion 7 - fun taxdue tax wage = wage*tax div 100; > val taxdue = fn: int->(int->int) taxdue tar en int som parameter och returnerar en funktion av typ int-> int (taxdue 40) är en funktion som tar en parameter wage och beräknar wage*40 div 100 taxdue kan appliceras partiellt, på bara en parameter, för att skapa en ny funktion - val tax20 = taxdue 20; > val tax20: fn:int->int - tax20 10000; > val it=2000:int 8 Template för PA funktioner Map Generellt mönster för deklaration av högre ordningens (PA) funktioner: fun fname pat 11 pat 12 pat 1n = exp 1 fname pat 21 pat 22 pat 2n = exp 2 fname pat k1 pat k2 pat kn = exp k Samma effekt som en funktion med n parametrar, men kan appliceras partiellt. Exempel: Kolla om lista1 är ett prefix av lista2 fun starts1 [ ] _ = true starts1 _ [ ] = false starts1 (h1::t1)(h2::t2) = h1=h2 andalso starts1 t1 t2 PA funktioner är en delmängd av HOF Map tar inte bara en funktion som parameter, men är också partiellt applicerbar. - fun map f [] = [] map f (x::xs)=(f x)::(map f xs); val map=fn: ( a-> b) -> a list -> b list sq : int->int map sq : int list -> int list floor: real->int map floor: real list -> int list 9 10 Anonyma funktioner Ett funktionsvärde kan existeras utan att namnges. Nyckelordet fn används - fn n=>2*n; > val it=fn:int->int - (fn n=>2*n) 3; > val it=6:int Generellt utseende fn pattern 1 => expression 1 pattern 2 => expression 2 pattern N => expression N Anonyma funktioner När ett funktionsvärde av denna typ appliceras på ett argument v matchas v mot mönstren, detta motsvarar evaluering av case v of pattern 1 => expression 1 pattern 2 => expression 2 pattern N => expression N 11 12
Anonyma funktioner Följande par av deklarationer är inbördes ekvivalenta - val CircleArea= fn r => Math.pi*r*r; - fun CircleArea r = Math.pi*r*r; > val CircleArea = fn:real->real - val f =fn(a,b)=> a*b; - fun f(a,b)= a*b; > val f = fn:int*int->int rec Följande definition fungerar ej: val fact= fn 0=>1 n=>n*fact(n-1); Vi måste använda nyckelordet rec för en rekursiv definition val rec fact= fn 0=>1 n=>n*fact(n-1); - val f=fn a=>(fn b=>a*b); - fun f a b=a*b; > val f = fn:int->int->int 13 14 mappning Anonyma funktioner kan med fördel mappas på listor mm. Ex: fun poslist []=[] poslist (x::xs)=x>0::poslist xs; är ekvivalent med fun poslist lst=map (fn x=> x>0) lst; eller val poslist = map (fn x=> x>0); op Infixa funktioner (på par) kan användas i prefix notation genom nyckelordet op. op +; val it=fn:int*int->int (op +) (2,3); val it=5:int; fun addelems [] = [] addelems ((x,y)::zs) = (x+y)::addelems zs; är ekvivalent med val addelements = map (op +); 15 16 HOF på listor map är en HOF på listor Filter: Ta fram alla element med en viss egenskap. Givet ett villkor, gå igenom listan och behåll de element som uppfyller villkoret -fun filter p []=[] filter p (x::xs)=if p x then x::filter p xs else filter p xs; > val filter = fn:( a->bool)->( a list-> a list) - filter (fn n=> n mod 2 = 0) [1,2,3,4,5,6,7]; > val it = [2,4,6]:int list HOF på listor Filter finns i biblioteket List, liksom ett antal andra nyttiga funktioner som tex: exists kollar om det finns ett element i en lista som uppfyller ett predikat val exists= fn:( a->bool)-> a list->bool all kollar om alla element i en lista uppfyller ett predikat val all = fn:( a->bool)-> a list->bool partition delar upp en lista i två (beroende på en predikatfunktion) (jfr unzip) val partition = fn:( a->bool)-> a list ->( a list * a list) 17 18
Sortering Insättningssortering Ta ut första elementet ur listan, sortera resten av listan och sätt in det första elementet på rätt ställe local fun insert x [] =[x] insert x (y::ys)=if x<y then x::y::ys else y::(insert x ys) in - fun sort []=[] sort (x::xs)=insert x (sort xs) > val sort=fn: a list-> a list Quicksort Välj ett element (tex första), dela upp listan i element som är större än det valda och de som är mindre. Sortera delarna och lägg sedan ihop dem. fun quick []=[] quick [x]=[x] quick (x::xs)= let val small = filter (fn y=>y<=x) xs val large = filter (fn y=>y>x) xs in quick small @ [x] @ quick large 19 20 foldr / foldl (överkurs) Listreduktion, ex summan av en lista Effekten av foldr (även kallad reduce) kan beskrivas som att vi sätter in mellan varje element i listan x1 x2 xn id Med prefix funktionsapplikation kan samma sak beskrivas som x1 ( x2 ( ( xn id) )) Elementen i listan summeras från höger till vänster. (stackrekursiv) foldr - fun sum xs = foldr (op +) 0 xs; > val sum = fn: int list -> int; - sum [1,3,5,7] > val it = 16:int - val sum=foldr (op +) 0; > val sum=fn:int list->int 21 22 foldl Vi kan också definiera en syskon-funktion till foldr som reducerar från vänster till höger. foldl f id [x1,x2,,xn]= f(xn, f(x2,f(x1,id)) ) foldl och foldr är utbytbara endast om den funktion som distribueras är kommutativ Jämför (1+(2+(3+0))) och (3+(2+(1+0))) Exempel Ex: logiskt and (foldl / foldr spelar ej roll) - fun andlist lst = foldl (fn(a,b) => a andalso b) true lst; - andlist [true, true, true]; > val it = true:bool Exempel: insättningssortering - fun insert (x,[]) = [x] insert (x,(y::ys)) = if x<y then x::y::ys else y::insert (x,ys); - val sort = foldl insert []; > val sort = fn : int list -> int list - sort [5, 8, 4, 9, 2, 1, 8, 7]; > val it = [1, 2, 4, 5, 7, 8, 8, 9] : int list 23 24
Funktionskomposition Applicera en funktion på resultatet av en funktionsapplikation, ex sin(cos x) f(g(x)) kan skrivas som (f o g) x infix 3 o; fun (f o g) x=f(g(x)); x har samma typ som domäntypen hos g g x har samma typ som domäntypen hos f Resultatet har samma typ som resultattypen hos f f: a-> b g: c-> a x: c o:( a-> b)*( c-> a)-> c-> b sqrt o real; val it=fn:int->real (sqrt o real) 16; val it=4.0:real o är fördefinierad i ML Funktionskomposition 25 26