270 likes | 388 Views
Mönstermatchning och rekursion. Nr 4. Förenklad notation. val fnname = fn name => expression Förenklas till fun fnname name = expression Exempel fun tax sum = sum * 25 div 100 > val tax = fn : int -> int fun sq (x:int) = x * x > val sq = fn : int -> int. Flera steg.
E N D
Förenklad notation valfnname = fnname => expression • Förenklas till funfnnamename = expression • Exempel fun tax sum = sum * 25 div 100 > val tax = fn : int -> int fun sq (x:int) = x * x > val sq = fn : int -> int
Flera steg - val taxdue = fn sum => fn tax => sum * tax div 100 - fun taxdue sum = fn tax => sum * tax div 100 - fun taxdue sum tax = sum * tax div 100 > val taxdue = fn : int -> int -> int - val compose = fn f => fn g => fn x => f (g x) - fun compose f = fn g => fn x => f (g x) - fun compose f g = fn x => f (g x) - fun compose f g x = f (g x) > compose = fn : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
Droppa parenteser • Funktionsapplikation associerar till vänster • f a b c = ((f a) b) c - twice sq 3 > 81 : int - sq sq 3; ! Toplevel input: ! sq sq 3; ! ^^ ! Type clash: expression of type ! int -> int ! cannot have type ! Int - sq (sq 3); > 81 : int
Villkorsuttryck • I beräkningar behöver vi möjlighet att välja olika resultat beroende på argumentens värde. if expression1 then expression2 else expression3 • expression1 måste returnera ett boolesk värde • expression2 och expression3 måste returnera värden av samma typ • Om expression1 evalueras till true är värdet av hela uttrycket värdet av expression2. • Om expression1 evalueras till false är värdet av hela uttrycket värdet av expression3.
Exempel - fun abs x = if x >= 0 then x else ~x > val abs : int -> int - fun toupper c = if c >= "a" andalso c <= "z" then chr (ord c - (ord "a" - ord "A")) else c > val toupper = fn : string -> string - toupper "c"; > "C" : string - toupper "å"; > "å" : string
Mönstermatchning • Låter oss jämföra argumentet med en mall ett mönster. Utseendet på argumentet får avgöra vilken väg som ska väljas. • Ett alternativ till villkorssatser • Hjälper programmeraren att kontrollera att alla fall hanteras • Ett sätt att dekonstruera (plocka isär) sammansatta värden • Ökar läsbarheten hos många program
En funktion med flera alternativ mönster fnpattern1=>expression1 |pattern2=>expression2 : : |patternN=>expressionN • Till att börja med kan ett mönster (patterni ) vara antingen en konstant eller en variabel eller en tuppel. • Alla mönster måste ha samma typ och uttrycken måste returnera samma typ.
Evaluering • När en funktion anropas jämförs (matchas) argumentets värde mot mönstren ett i taget • Om mönstret är en konstant så matchar bara samma värde • Om mönstret är en variabel matchar alla värden och variabeln binds till värdet. • Om mönstret är en tuppel måste argumentvärdet också vara det och varje del i tuppeln matchas på samma sätt • Sedan evalueras motsvarande uttryck.
Förenklad form val name = fnpattern1=>fkn_body1 |pattern2=>fkn_body2 : : |patternN=>fkn_bodyN • Motsvarar funname pattern1=fkn_body1 |name pattern2=fkn_body2 : : : |name patternN=fkn_bodyN
Exempel fun not x = if x (* = true *) then false else true; val not = fn : bool -> bool fun not true = false | not false = true val not = fn : bool -> bool fun zero 0 = true | zero n = false > val zero = fn : int -> bool
Strängar fun past "stand" = "stood" | past "swim" = "swam" | past "eat" = "ate" | past v = v ^ "ed" val past = fn : string -> string - past "eat"; > "ate" : string - past "talk"; > "talked" : string
Mer strängar fun change "red" = "red & yellow" | change "red & yellow" = "green" | change "green" = "yellow" | change "yellow" = "red" | change s = "illegal light: "^s > val change = fn : string -> string - twice change "red"; > "green" : string - change "blue"; > "illegal light: blue" : string
Undantag • För att avbryta beräkningar - 1 div 0; uncaught exception Div • Egen definierade undantag exception name • Använda undantag raise name
Exempel exception Illegal_light exception Illegal_light fun change "red" = "red & yellow" | change "red & yellow" = "green" | change "green" = "yellow" | change "yellow" = "red" | change s = raise Illegal_light > val change = fn : string -> string - change "blue"; uncaught exception Illegal_light
Wildcard • Namnlös parameter • När den formella parametern inte används kan man ersätta den med Wildcard som skrivs som "_". • Wildcard matchar alla värden men det sker ingen bindning till argumentet. fun zero 0 = true | zero _ = false • Använd för att markera att en parameter inte används i funktionskroppen.
Matcha flera variabler val And = fn false => (fn false => false | true => false) | true => (fn false => false | true => true val And = fn : bool -> bool -> bool fun And false false = false | And false true = false | And true false = false | And true true = true fun And false _ = false | And true b = b
Rekursion • Att upprepa en beräkning • Rekursion bygger på att en funktion anropar sig själv, det skapar nya instanser av de bundna variablerna med nya värden. • För rekursion krävs ett basfall som inte anropar funktionen och minst ett rekursivt fall. • Basfallet kan ofta beskrivas med mönster t ex en heltalskonstant
Rekursion över Naturliga tal (0 1 2 3 4 …) • Basfall är ofta 0 och det rekursiva fallet för positiva heltal. • För att göra något n gånger: • för 0 gånger : gör det inte • för n gånger : gör det en gång sedan gör det (n-1) gånger fun fnname 0 = base_case | fnname n = recursive case usingfname(n-1)
Exempel fun sum 0 = 0 | sum n = n + sum (n-1) > val sum = fn : int -> int sum 4 ==> 4 + sum (4-1) ==> 4 + sum 3 ==> 4 + (3 + sum 2) ==> 4 + (3 + (2 + sum 1)) ==> 4 + (3 + (2 + (1 + sum 0))) ==> 4 + (3 + (2 + (1 + 0))) ==> 10
2 upphöjt till n fun power2 0 = 1 | power2 n = 2 * power2 (n-1) val power2 = fn : int -> int power2 3 ==> 2 * (power2 2) ==> 2 * (2 * (power2 1)) ==> 2 * (2 * (2 * (power2 0))) ==> 2 * (2 * (2 * 1)) ==> 8
x upphöjt till n • Abstrahera ut 2 och få x upphöjt till n fun power x 0 = 1 | power x n = x * power x (n-1) val power = fn : int -> int -> int val power2 = power 2 val power2 = fn : int -> int
Generaliserad summa • Summan av kvadraten av alla tal mellan 0 och n fun sumsq 0 = 0 | sumsq n = sq n + sumsq (n-1) val sumsq = fn : int -> int sumsq 3 ==> sq 3 + sumsq 2 ==> 9 + sumsq 2 ==> 9 + (sq 2 + sumsq 1) ==> 9 + (4 + sumsq 1) ==> 9 + (4 + (sq 1 + sumsq 0)) ==> 9 + (4 + (1 + sumsq 0)) ==> 9 + (4 + (1 + 0)) ==> 14
Fler fall • Summan av dubbla värde av alla tal mellan 0 och n fun double n = 2 * n fun sumdouble 0 = 0 | sumdouble n = double n + sumdouble (n-1) • Generalisera : ersätt double och sq med namnet f • Abstrahera: låt f vara en formell parameter fun sumfunc f 0 = 0 | sumfunc f n = f n + sumfunc f (n-1)
Specialisera • Specialisera för att få de tidigare funktionerna fun id x = x val sum = sumfunc id val sumsq = sumfunc sq val sumdouble = sumfunc double
Exempel sumdouble 3 ==> sumfunc double 3 ==> double 3 + sumfunc double 2 ==> 6 + sumfunc double 2 ==> 6 + (double 2 + sumfunc double 1) ==> 6 + (4 + sumfunc double 1) ==> 6 + (4 + (double 1 + sumfunc 0)) ==> 6 + (4 + (2 + sumfunc 0) ==> 6 + (4 + (2 + 0) ==> 12
Tre grundläggande metoder • Sekvens • Först gör man en sak sedan nästa • ML: funktionskomposition, f (g x) applicera först g på x därefter f på resultatet • Val • Alternativa beräkningar beroende på data • ML: if-uttryck, mönstermatchning, (case-uttryck) • Upprepning • Samma beräkning flera gånger med olika data • ML: rekursion