350 likes | 627 Views
Higher-order functions in ML (Ch. 9). Functionals. More pattern matching Function values and anonymous functions Higher-order functions and currying Predefined higher-order functions. Pattern-Matching in the case expression. fun f 0 = "zero" | f _ = "non-zero";. match. fun f n =
E N D
Higher-order functions in ML(Ch. 9) Functionals • More pattern matching • Function values and anonymous functions • Higher-order functions and currying • Predefined higher-order functions IT 327
Pattern-Matching in the case expression fun f 0 = "zero" | f _ = "non-zero"; match fun f n = case n of 0 => "zero" | _ => "non-zero"; IT 327
Case Expressions <case-expr> ::= case <expression> of <match> <match> ::= <rule> <match> ::= <rule> |<match> <rule> ::= <pattern> => <expression> - case 1+1 of = 3 => "three" | = 2 => "two" | = _ => "hmm"; val it = "two" : string case x of _::_::c::_ => c | _::b::_ => b | a::_ => a | nil => 0 IT 327
Generalizes if • The two expressions above are equivalent • So if-then-else is really just a special case of case if exp1 then exp2 else exp3 case exp1 of true => exp2 | false => exp3 IT 327
Behind the Scenes • if := an abbreviation of case • This explains some odd SML/NJ error messages: - if 1=1 then 1 else 1.0; Error: types of if branches do not agree [literal] then branch: int else branch: real in expression: if 1 = 1 then 1 else 1.0 - if 1=1 then 1 else 1.0; Error: types of rules don't agree [literal] earlier rule(s): bool -> int this rule: bool -> real in rule: false => 1.0 New version of SML shows different message IT 327
Predefined Functions • Predefined variables bound to functions: - ord;val it = fn : char -> int- ~;val it = fn : int -> int - val x = ~;val x = fn : int -> int-x 3;val it = ~3 : int IT 327
Quick Sort with < < < < < < < < < < < < < < < < < < < < < < < < < < < IT 327
Quick Sort with > We can make a function parameter for the meaning of ordering > > > > > > > > > > > > > > > > > > > > > > > > > > IT 327
Quick Sort in ML fun split nil = (nil,nil) | split [a] = ([a],nil) | split [a,b] = if (a <= b) then ([a],[b]) else ([b],[a]) | split (a::b::c) = let val (x,y) = split (a::c) in if (a < b) then (x, b::y) else (b::x, y) end; fun quicksort nil = nil | quicksort [a] = [a] | quicksort a = let val (x,y) = split a in quicksort(x)@quicksort(y) end; This is more conceptual to the understanding of the problem IT 327
Quick Sort with function parameter fun split (nil, _) = (nil,nil) | split ([a], _) = ([a],nil) | split ([a,b], f) = if f(a,b) then ([a],[b]) else ([b],[a]) | split (a::b::c, f) = let val (x,y) = split (a::c, f) in if f(a,b) then (x, b::y) else (b::x,y) end; fun quicksort (nil,_) = nil | quicksort ([a],_) = [a] | quicksort (a,f) = let val (x,y) = split (a,f) in quicksort(x,f)@quicksort(y,f) end; IT 327
Anonymous Functions functions without a name -calculus IT 327
Function Values • Functions in ML do not have names but can be given. • The fun syntax does two separate things: • Creates a new function value • Binds that function value to a name - fun f x = x + 2;val f=fn : int -> int- f 1;val it = 3 : int IT 327
Anonymous Functions in ML • Named function: • Anonymous function: - fun f x = x + 2;val f = fn : int -> int- f 1;val it = 3 : int A match - fn x => x + 2;val it = fn : int -> int- (fn x => x + 2) 1;val it = 3 : int IT 327
The fn Syntax <fun-expr> ::= fn <match> • We use fn to give an expression whose value is an (anonymous) function • The followings are the same effect: • fun f x = x + 2 • val f =fn x => x + 2 IT 327
Using Anonymous Functions: - fun less (a,b) = a < b;val intBefore = fn : int * int -> bool- quicksort ([1,4,3,2,5], less);val it = [1,2,3,4,5] : int list - quicksort ([1,4,3,2,5], fn (a,b) => a<b);val it = [1,2,3,4,5] : int list- quicksort ([1,4,3,2,5], fn (a,b) => a>b);val it = [5,4,3,2,1] : int list Q: Can we do the same in Java or C++? IT 327
The op keyword - *; Error: expression or pattern begins with infix identifier "*" • Binary operators are special functions • op gives the underlying function - op *; val it = fn : int * int -> int - op <; val it = fn : int * int -> bool - quicksort ([1,4,3,2,5], op<); val it = [1,2,3,4,5] : int list IT 327
Higher-order Functions • Every function has an order: • A function that does not take any functions as parameters, and does not return a function value, has order 1 • A function that takes a function as a parameter or returns a function value has order n+1, where n is the order of its highest-order parameter or returned value • The quicksort we just saw is a second-order function A little digression with more precise notions on higher-ordered Functionals (CiE 2006) IT 327
Examples: -> is right-associative 1st order int * int -> bool int list * (int * int -> bool) -> int list int -> int -> int (int -> int) * (int -> int) -> (int -> int) int -> bool -> real -> string ('a -> 'b) * ('c -> 'a) -> 'c -> 'b int -> int -> int -> int -> int -> int 2nd order 2nd order 2nd order 3rd order 2nd order 5th order IT 327
Haskell B. Curry (1900-1982) Curry-Howard logic Currying • a 2-tuple looks like two parameters:fun f (a,b) = a + b; • Another way: fun g a = fn b => a+b; • The general name for this is currying f (2,3); g 2 3; (fn b => 2+b) 3 5 IT 327
Curried Addition -> is right-associative - fun f (a,b) = a+b; val f = fn : int * int -> int - fun g a = fn b => a+b; val g = fn : int -> int -> int - f(2,3); val it = 5 : int - g 2 3; val it = 5 : int • function application is left-associative • So g 2 3 means ((g 2) 3) IT 327
Advantages • No tuples: we get to write g 2 3 instead of f(2,3) • The real advantage: we can specialize functions for particular initial parameters - fun g a = fn b => a+b; • val add2 = g 2;val add2 = fn : int -> int - add2 3;val it = 5 : int- add2 10;val it = 12 : int S-m-n Theorem IT 327
Example: • quicksort (op <) [1,4,3,2,5];val it = [1,2,3,4,5] : int list • val sortBackward = quicksort (op >);val sortBackward = fn : int list -> int list - sortBackward [1,4,3,2,5];val it = [5,4,3,2,1] : int list • val sortForward = quicksort (op <);val sortBackward = fn : int list -> int list - sortForward [1,4,3,2,5];val it = [1,2,3,4,5] : int list • Let quicksort be written in the Currying form, in which the comparison function is a first, curried parameter. IT 327
Multiple Curried Parameters • fun f (a,b,c) = a+b+c;val f = fn : int * int * int -> int - fun g a = fn b => fn c => a+b+c; val g = fn : int -> int -> int -> int • f (1,2,3);val it = 6 : int • g 1 2 3;val it = 6 : int IT 327
Notation For Currying • Explicit intermediate anonymous functions • There is a simpler notation much easier to read and write fun g a = fn b => fn c => a+b+c; IT 327
Easier Notation for Currying fun f a = fn b => a+b; same as: fun f a b = a+b; fun g a = fn b => fn c => a+b+c; Same as: fun g a b c = a+b+c; • fun f a b c d = a+b+c+d; val f = fn : int -> int -> int -> int -> int IT 327
Predefined Higher-Order Functions • map (map a function to a list) • foldr (fold an operation from the right) • foldl (fold an operation from the left) IT 327
The map Function • Apply a function to every element of a list, and collect a list of results - map ~ [1,2,3,4];val it = [~1,~2,~3,~4] : int list - map (fn x => x+1) [1,2,3,4];val it = [2,3,4,5] : int list - map (fn x => x mod 2 = 0) [1,2,3,4];val it = [false,true,false,true] : bool list - map (op +) [(1,2),(3,4),(5,6)];val it = [3,7,11] : int list IT 327
The map Function Is Curried - map; val it = fn : ('a -> 'b) -> 'a list -> 'b list - map (fn x => x+1)[1,2,3,4];val it = [2,3,4,5] : int list • val f = map (op +);val f = fn : (int * int) list -> int list - f [(1,2),(3,4)];val it = [3,7] : int list IT 327
Define your own map fun mymap _ nil = nil | mymap f (a::t) = f a:: mymap f t; IT 327
The foldr Function Perform the binary operation on all the elements of a list from the right(tail) foldr f c x where x = [x1, …, xn] and computes: foldr (op +) 0 [1,2,3,4] evaluates as 1+(2+(3+(4+0)))=10 IT 327
Examples • foldr (op +) 0 [1,2,3,4];val it = 10 : int • foldr (op * ) 1 [1,2,3,4];val it = 24 : int • foldr (op ^) "" ["abc","def","ghi"];val it = "abcdefghi" : string - foldr (op ::) [5] [1,2,3,4];val it = [1,2,3,4,5] : int list 4::[5] IT 327
The foldr Function Is Curried - foldr; val it = fn : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b - foldr (op +); val it = fn : int -> int list -> int - foldr (op +) 0; val it = fn : int list -> int - val sum = foldr (op +) 0; val sum = fn : int list -> int - sum [1,2,3,4,5]; val it = 15 : int IT 327
The foldl Function Perform the binary operation on all the elements of a list from theleft(head) foldl f c x where x = [x1, …, xn] and computes: So foldl (op +) 0 [1,2,3,4] 4+(3+(2+(1+0)))=10 foldr (op +) 0 [1,2,3,4] 1+(2+(3+(4+0)))=10 IT 327
Commutative • ^ is not commutative • foldl and foldr are the same if the function is associative and commutative, like + and * • foldr (op ^) "" ["abc","def","ghi"];val it = "abcdefghi" : string • foldl (op ^) "" ["abc","def","ghi"];val it = "ghidefabc" : string • foldr (op -) 0 [1,2,3,4];val it = ~2 : int - foldl (op -) 0 [1,2,3,4];val it = 2 : int • – is neither associative nor commutative IT 327
Homework 4 Chapter 9. Exercises: 3, 6, 10, 26, 27, 28 plus 4 questions to be announced. IT 327