380 likes | 463 Vues
Högre ordningens listfunktioner nr 6. Ackumulerande parametrar Map, filter reduce. Ackumulerande parametrar. En parameter för att skicka tillfällig information mellan olika rekusions steg i en rekursiv funktion Varför: Lagra tillfälliga värden och bära delresultat För att hantera tillstånd
E N D
Högre ordningens listfunktionernr 6 Ackumulerande parametrar Map, filter reduce
Ackumulerande parametrar • En parameter för att skicka tillfällig information mellan olika rekusions steg i en rekursiv funktion • Varför: • Lagra tillfälliga värden och bära delresultat • För att hantera tillstånd • Kontrollera rekursion.
Lagra tillfälliga värden • Största elementet i en lista givet det hittills största fun max1 bsf [] = bsf : int | max1 bsf (x::xs) = if x > bsf then max1 x xs else max1 bsf xs > val max1 = fn : int -> int list -> int • Om det finns minst ett element i listan kan vi hitta det största, för en tom lista blir det ett undantag exception Max > exception Max fun max (x :: xs) = max1 x xs | max [] = raise Max > val max = fn : int list -> int
Test max [1,4,2,6,5,7,2] ==> max1 1 [4,2,6,5,7,2] ==> max1 4 [2,6,5,7,2] ==> max1 4 [6,5,7,2] ==> max1 6 [5,7,2] ==> max1 6 [7,2] ==> max1 7 [2] ==> max1 7 [] ==> 7 - max []; • uncaught exception Max
Lagra delresultat • Summera heltalen i en lista, lagra delresultaten i en parameter. Antag att alla värden i början är summerade i en parameter, tag sedan hand om resten av listan fun suma (s:int) (x::xs) = suma (s+x) xs | suma s [] = s > val suma = fn : int -> int list -> int • För att summera en lista låt delsumman vara noll till att börja med fun sumacc xs = suma 0 xs val sumacc = suma 0 > val sumacc = int list -> int
sum [1,2,3,4] ==> 1 + sum [2,3,4] ==> 1 + (2 + sum [3,4] ==> 1 + (2 + (3 + sum [4]) ==> 1 + (2 + (3 + (4 + sum []))) ==> 1 + (2 + (3 + (4 + 0))) ==> 1 + (2 + (3 + 4)) ==> 1 + (2 + 7) ==> 1 + 9 ==> 10 sumacc [1,2,3,4] ==> suma 0 [1,2,3,4] ==> suma (0+1) [2,3,4] ==> suma (1) [2,3,4] ==> suma (1+2) [3,4] ==> suma (3) [3,4] ==> suma (3+3) [,4] ==> suma (6) [4] ==> suma (6+4) [] ==> suma (10) [] ==> 10 Jämförelse mellan sum och sumacc
Hantera tillstånd • Ett tillstånd som förändras vid varje rekursivt anrop hanteras av en parameter. Tillståndet används för att beräkna slutresultatet. fun fname' state [] = MAKERESULT state | fname' state (x::xs) = fname' (TRANSFORMSTATE state x) xs val fname = fname' START_STATE
Exempel: • Snittpris per enhet i ett lager beroende på antal av varje enhet fun average' (ta, tp) [] = tp / real ta | average' (ta, tp) ((a,p) :: aps) = average' (ta + a,tp + real a * p) aps > val average' = fn : int * real -> (int * real) list -> real val average = average' (0, 0.0) > val average = fn : (int * real) list -> real average [(50,23.0),(100,12.0),(67,9.0)]; > val it = 13.6082949308756 : real
Kontrollera rekursion • Motsvarar loop-index, t ex en räknare för att avgöra hur många rekursiva anrop som skall göras. • Beräkna xn fun power x 0 = 1.0 | power x n = x * power x (n-1) > val power = fn : real -> int -> real
Generell upprepning fun iter f 0 s = s | iter f n s = iter f (n-1) (f s) > val iter = fn : ('a -> 'a) -> int -> 'a -> 'a • Beräkna xn fun power x n = iter (fn y => x * y) n 1.0 > val power = fn : real -> int -> real power 7.0 12; > 1.38413E+10 : real
twice igen! fun twice f = iter f 2 twice sq n ==> iter sq 2 n ==> iter sq 1 (sq n) ==> iter sq 0 (sq (sq n)) ==> sq (sq n)
Inkapsling • Ett stor program bör delas in i enheter som är så oberoende av varandra som möjligt. • Från en sådan enhet bör endast de viktiga bitarna vara synliga utåt. De bitar som ska användas i andra delar av programmet. • De delar som endast används i enheten bör inte vara synliga utifrån. • Den som använder enheten ska inte behöva sätta sig in i detaljerna i hur enheten fungerar, utan enbart vilken effekt de synliga delarna har.
Lokala deklarationer i ML • Den slutliga funktionen anropar ofta flera hjälpfunktioner för att lösa problemet. Dessa kan paketeras ihop och blir synliga endast för huvudfunktionen men inte utanför. local declarations1 in declarations2 end • Deklarationerna i declarations1 är endast synliga fram till end i deklarationen.
Exempel local fun suma (s:int) (x::xs) = suma (s+x) xs | suma s [] = s in val sumacc = suma 0 end val sumacc = fn : int list -> int
Lokala deklarationer i uttryck • Man kan ge namn åt tillfälliga värden inuti uttryck, det kan vara konstanta värden men också värden beroende av parametrar eller beräkningar som förekommer på flera ställen. let declarations in expression end • Värdet av lokalt deklarerade namnet är synligt endast fram till end i uttrycket. • Värdet av hela let-uttrycket är värdet av uttrycket expression.
Exempel let val x = 10 in 2*x*x end; > val it = 200:int
Ekvationslösning • Lösning av 2:a-gradsekvation (ax2 + bx + c = 0) exception Solve fun solve a b c = let val p = b*b -4.0*a*c in if p >= 0.0 then ((~b+sqrt p)/(2.0*a),(~b-sqrt p)/(2.0*a)) else raise Solve end; > val solve = fn: real->real->real-> (real * real) solve 1.0 2.0 ~3.0; val it = (1.0,~3.0) : real * real
Sekvenser • En lista med kvadraten på alla tal mellan 1 och n [sq 1, sq 2, sq 3, … , sq n] • Först en funktion som genererar en lista med kvadraten på alla tal mellan m och k. fun sqlist1 m k = if m > k then [] else sq m :: sqlist1 (m+1) k val sqlist1 = fn : int -> int -> int list • Den används för att skapa en funktion från 1 till n val sqlist = sqlist 1
Med lokala deklarationer local fun sqlist1 m k = if m > k then [] else sq m :: sqlist1 (m+1) k in val sqlist = sqlist1 1 end > val sqlist = fn : int -> int list sqlist 10; > val it = [1,4,9,16,25,36,49,64,81,100] : int list
Vända på en lista • Att vända på en lista kan ses som att hela tiden flytta första elementet till en annan lista. • Jämför med att flytta böckerna i en hög en efter en till en ny hög. Ordningen i den nya högen kommer att vara den motsatt mot ursprungliga högen. local fun rev1 rlsf [] = rlsf | rev1 rlsf (x::xs) = rev1 (x::rlsf) xs in val rev = rev1 [] end val rev = fn : 'a list -> 'a list
Exempel rev [1,2,3] ==> rev1 [] [1,2,3] ==> rev1 [1] [2,3] ==> rev1 [2,1] [3] ==> rev1 [3,2,1] [] ==> [3,2,1]
Map • Ofta vill man utföra samma operation f på alla element i en lista • Givet: [x1, x2, …, xn] och en funktion f • Resultat: [f x1, f x2, …, f xn] • Kvadraten på alla heltal i en lista fun sqs [] = [] | sqs (x::xs) = sq x :: sqs xs • Denna form kan generaliseras fun map _ [] = [] | map f (x::xs) = f x :: map f xs val map = fn : ('a -> 'b) -> 'a list -> 'b list
Utvidgning • Map kan ses som en utvidgning av en funktion från att gälla på enstaka värden till att gälla för listor av sådana värden. sq : int -> int map sq : int list -> int list floor : real -> int map floor : real list -> int list
Exempel sqs [2,4,6] ==> map sq [2,4,6] ==> sq 2 :: map sq [4,6] ==> 4 :: map sq [4,6] ==> 4 :: sq 4 :: map sq [6] ==> 4 :: 16 :: map sq [6] ==> 4 :: 16 :: sq 6 :: map sq [] ==> 4 :: 16 :: 36 :: map sq [] ==> 4 :: 16 :: 36 :: [] ==> [4,16,36]
Filter • Om man vill plocka bort vissa element från en lista givet något villkor, så kan man gå igenom listan och endast behålla de som uppfyller villkoret. • Behåll endast de jämna talen fun even n = n mod 2 = 0 val even = fn : int -> bool fun evens [] = [] | evens (x::xs) = if even x then x :: evens xs else evens xs val evens = fn : int list -> int list
Generalisera • Abstrahera ut villkorsfunktionen (predikatet) fun filter _ [] = [] | 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 val evens = filter even
Exempel evens [1,2,3,4] ==> filter even [1,2,3,4] ==> if even 1 then 1 :: filter even [2,3,4] else filter even [2,3,4] ==> filter even [2,3,4] ==> if even 2 then 2 :: filter even [3,4] else filter even [3,4] ==> 2 :: filter even [3,4] ==> 2 :: (if even 3 then 3 :: filter even [4] else filter even [4]) ==> 2 :: filter even [4] ==> 2 :: (if even 4 then 4 :: filter even [] else filter even []) ==> 2 :: 4 :: filter even [] ==> 2 :: 4 :: [] ==> [2,4]
Primtal fun fromto n m = if n > m then [] else n :: fromto (n+1) m val fromto = fn : int -> (int -> int list) fun divides n m = m mod n = 0 val divides = fn : int -> (int -> bool) fun sieve [] = [] | sieve (x::xs) = x :: sieve (filter (not o divides x) xs) val sieve = fn : int list -> int list fun primes m = sieve (fromto 2 m) val primes = fn : int -> int list - primes 30; > val it = [2,3,5,7,11,13,17,19,23,29] : int list
Ordnade listor • Det är vanligt att arbeta med ordnade listor. • För att kontrollera att en lista är ordnad fun iorder [] = true | iorder [(x:int)] = true | iorder (x1::x2::xs) = x1 <= x2 andalso iorder (x2::xs) val iorder = fn : int list -> bool
Exempel iorder [1,3,5,6] ==> 1 <= 3 andalso iorder [3,5,6] ==> true andalso iorder [3,5,6] ==> iorder [3,5,6] ==> 3 <= 5 andalso iorder [5,6] ==> iorder [5,6] ==> 5 <= 6 andalso iorder [6] ==> iorder [6] ==> true
Generaliserad ordning • Det som gör iorder specifik för heltal är "<=" om man abstraherar den få man en ordningskontroll för listor som är generell fun order _ [] = true | order _ [x] = true | order p (x1::x2::xs) = p x1 x2 andalso order p (x2::xs) val order = fn : ('a -> 'a -> bool) -> 'a list -> bool fun sle (x:string) y = x <= y val iorder = order (fn (x:int) => fn y => x <= y) val sorder = order sle
Insättning i en ordnad lista • Om listan är ordnad med stigande värden så förblir den ordnad om jag sätter in ett element före det första element som är större än element som jag vill sätta in. fun inserti (v:int) [] = [v] | inserti v (x::xs) = if v <= x then v :: x :: xs else x :: inserti v xs val inserti = fn : int -> int list -> int list
Generaliserad insättning • Om jag ersätter jämförelsen med en funktion p som avgör vilket värde som är mindre, får jag en generell insättningsfunktion. fun insert _ v [] = [v] | insert p v (x::xs) = if p v x then v :: x :: xs else x :: insert p v xs val insert = fn:('a->'a->bool)->'a->'a list->'a list
Specialicering val inserti = insert (fn (x:int) => fn y => x <= y) val inserti = fn : int -> int list -> int list val inserts = insert sle val inserts=fn:string->stringlist->stringlist fun pairle ((x1:int),(y1:int)) (x2,y2) = x1 <= x2 orelse (x1 = x2 andalso y1 <= y2) val pairle = fn : int * int -> int * int -> bool val insertp = insert pairle val insertp = fn:int*int->(int*int)list->(int*int)list
Insertion sort • Ta ut första elementet ur listan, sortera resten av listan och sätt sedan in det första elementet (i ordning) i den sorterade listan. fun isort [] = [] | isort (x::xs) = inserti x (isort xs) val isort = fn : int list -> int list • Generaliserar över insättningsfunktionen. fun sort insert [] = [] | sort insert (x::xs) = insert x (sort insert xs) val sort = fn : ('a -> 'b list -> 'b list) -> 'a list -> 'b list
Specialisering • Specialicera för olika typer val isort = sort inserti val isort = fn : int list -> int list val ssort = sort inserts val ssort = fn : string list -> string list val psort = sort insertp val psort = fn : (int * int) list -> (int * int) list isort [2,5,1,7,4,9,2,4]; val it = [1,2,2,4,4,5,7,9] : int list
Reducering/Folding • Att reducera värdena i en lista till ett värde. fun add (x:int) y = x + y fun sum [] = 0 | sum (x::xs) = add x (sum xs) fun concat s1 s2 = s1^s2 fun concs [] = "" | concs (x::xs) = concat x (concs xs) • Generalisera funktionen och resultatet för tomma listan fun reduce f b [] = b | reduce f b (x::xs) = f x (reduce f b xs) valreduce=fn:('a->'b->'b)->'b->'alist->'b
Specialicera val sum = reduce add 0 val sum = fn : int list -> int val concs = reduce concat "" val concs = fn : string list -> string fun sort insert = reduce insert [] val sort = fn : ('a->('blist->'blist))->('alist->'blist) fun cons x xs = x::xs val cons = fn : 'a -> ('a list -> 'a list) fun map f = reduce (cons o f) [] val map = fn : ('a -> 'b) -> ('a list -> 'b list)