a third look at ml
DESCRIPTION
A Third Look At ML. Outline. More pattern matching Function values and anonymous functions Higher-order functions and currying Predefined higher-order functions. More Pattern-Matching. Last time we saw pattern-matching in function definitions: fun f 0 = "zero" | f _ = "non-zero"; - PowerPoint PPT PresentationTRANSCRIPT
A Third Look At MLA Third Look At ML
Chapter Nine Modern Programming Languages 1
OutlineOutlineMore pattern matchingFunction values and anonymous
functionsHigher-order functions and
curryingPredefined higher-order functions
Chapter Nine Modern Programming Languages 2
More Pattern-MatchingMore Pattern-MatchingLast time we saw pattern-
matching in function definitions:◦ fun f 0 = "zero"| f _ = "non-zero";
Pattern-matching occurs in several other kinds of ML expressions:◦ case n of 0 => "zero" | _ => "non-zero";
Chapter Nine Modern Programming Languages 3
Match SyntaxMatch SyntaxA rule is a piece of ML syntax that looks
like this:
A match consists of one or more rules separated by a vertical bar, like this:
Each rule in a match must have the same type of expression on the right-hand side
A match is not an expression by itself, but forms a part of several kinds of ML expressions
Chapter Nine Modern Programming Languages 4
<rule> ::= <pattern> => <expression>
<match> ::= <rule> | <rule> '|' <match>
Case ExpressionsCase Expressions
The syntax is
This is a very powerful case construct—unlike many languages, it does more than just compare with constants
Chapter Nine Modern Programming Languages 5
- case 1+1 of= 3 => "three" |= 2 => "two" |= _ => "hmm";val it = "two" : string
<case-expr> ::= case <expression> of <match>
ExampleExample
Chapter Nine Modern Programming Languages 6
case x of _::_::c::_ => c | _::b::_ => b | a::_ => a | nil => 0
The value of this expression is the third elementof the list x, if it has at least three, or the second element if x has only two, or the first element if x has only one, or 0 if x is empty.
Generalizes Generalizes ifif
The two expressions above are equivalent
So if-then-else is really just a special case of case
Chapter Nine Modern Programming Languages 7
if exp1 then exp2 else exp3
case exp1 of true => exp2 | false => exp3
Behind the ScenesBehind the ScenesExpressions using if are actually
treated as abbreviations for case expressions
This explains some odd SML/NJ error messages:
Chapter Nine Modern Programming Languages 8
- 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
OutlineOutlineMore pattern matchingFunction values and anonymous
functionsHigher-order functions and
curryingPredefined higher-order functions
Chapter Nine Modern Programming Languages 9
Predefined FunctionsPredefined Functions
When an ML language system starts, there are many predefined variables
Some are bound to functions:
Chapter Nine Modern Programming Languages 10
- ord;val it = fn : char -> int- ~;val it = fn : int -> int
Defining FunctionsDefining Functions
We have seen the fun notation for defining new named functions
You can also define new names for old functions, using val just as for other kinds of values:
Chapter Nine Modern Programming Languages 11
- val x = ~;val x = fn : int -> int- x 3;val it = ~3 : int
Function ValuesFunction ValuesFunctions in ML do not have
namesJust like other kinds of values,
function values bound to one or more variables
The fun syntax does two separate things:◦Creates a new function entity◦Binds that function entity to a name
Chapter Nine Modern Programming Languages 12
Anonymous FunctionsAnonymous FunctionsNamed function:
Anonymous function:
Chapter Nine Modern Programming Languages 13
- fun f x = x + 2;val f = fn : int -> int- f 1;val it = 3 : int
- fn x => x + 2;val it = fn : int -> int- (fn x => x + 2) 1;val it = 3 : int
The The fnfn Syntax SyntaxAnother use of the match syntax
Using fn, we get an expression whose value is an (anonymous) function
We can define what fun does in terms of val and fn
These two definitions have the same effect:◦ fun f x = x + 2◦ val f = fn x => x + 2
Chapter Nine Modern Programming Languages 14
<fun-expr> ::= fn <match>
a match
fnfn and and recrecA special syntax is required for binding
variables to recursive functions with fn◦ Since the expression is evaluated before
binding, and the function has no name, you can’t recurse directly
The rec keyword allows you to accomplish this
- val rec fact = fn 0 => 1 | n => n*fact(n-1);val fact = fn : int -> int
Chapter Nine Modern Programming Languages 15
Using Anonymous Using Anonymous FunctionsFunctionsOne simple application: when you
need a small function in just one place
Without fn:
With fn:
Chapter Nine Modern Programming Languages 16
- fun intBefore (a,b) = a < b;val intBefore = fn : int * int -> bool- quicksort ([1,4,3,2,5], intBefore);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
The The opop keyword keyword
Binary operators are special functionsSometimes you want to treat them
like plain functions: to pass <, for example, as an argument of type int * int -> bool
The keyword op before an operator gives you the underlying function
Chapter Nine Modern Programming Languages 17
- op *;val it = fn : int * int -> int- quicksort ([1,4,3,2,5], op <);val it = [1,2,3,4,5] : int list
Defining Binary Infix Defining Binary Infix ““OperatorsOperators””You can define functions that use
infix notation:◦x f y
Use the infix statement:◦infix [precedence: 0,3-7] <fun-
name>Define with infix syntax:
◦fun x f y = …
Chapter Nine Modern Programming Languages 18
Defining an Infix Defining an Infix plusplus FunctionFunction
Chapter Nine Modern Programming Languages 19
- infix 6 plus;infix 6 plus- fun x plus y = x + y;val plus = fn : int * int -> int- 2 plus 3;val it = 5 : int- val g = op plus;val g = fn : int * int -> int- g (2,3);val it = 5 : int
Operator PrecedenceOperator Precedence7: * / div mod6: + - ^5: :: @4: = <> < > <= >=Don’t worry about the others
Chapter Nine Modern Programming Languages 20
fnfn vs. vs. casecase
Chapter Nine Modern Programming Languages 21
The notation
case exp of pat1 => exp1 | ... | patn => expn
is shorthand for the function application
(fn pat1 => exp1 | ... | patn => expn) exp
Function Objects in C++Function Objects in C++
Chapter Nine Modern Programming Languages 22
Anonymous Functions in Anonymous Functions in C++0xC++0x
Chapter Nine Modern Programming Languages 23
OutlineOutlineMore pattern matchingFunction values and anonymous
functionsHigher-order functions and
curryingPredefined higher-order functions
Chapter Nine Modern Programming Languages 24
Higher-order FunctionsHigher-order FunctionsEvery 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 function parameter or returned value
The quicksort we just saw is a second-order function
Chapter Nine Modern Programming Languages 25
PracticePractice
Chapter Nine Modern Programming Languages 26
What is the order of functions with each of the following ML types?
int * int -> boolint list * (int * int -> bool) -> int listint -> int -> int(int -> int) * (int -> int) -> (int -> int)int -> bool -> real -> string
What can you say about the order of a function with this type?
('a -> 'b) * ('c -> 'a) -> 'c -> 'b
CurryingCurryingWe've seen how to get two
parameters into a function by passing a 2-tuple:
fun f (a,b) = a + b;
Another way is to write a function that takes the first argument, and returns another function that takes the second argument:
fun g a = fn b => a+b;
The general name for this is currying◦Arguments are supplied in successive
callsChapter Nine Modern Programming Languages 27
Curried AdditionCurried Addition
Remember that function application is left-associative
So g 2 3 means ((g 2) 3)
Chapter Nine Modern Programming Languages 28
- fun f (a,b) = a+b; (* not curried *)val f = fn : int * int -> int- fun g a = fn b => a+b; (* curried *)val g = fn : int -> int -> int- f(2,3);val it = 5 : int- g 2 3;val it = 5 : int
AdvantagesAdvantagesNo tuples: we get to write g 2 3 instead of f(2,3)
But the real advantage: we get to specialize functions for particular initial parameters
Chapter Nine Modern Programming Languages 29
- val add2 = g 2;val add2 = fn : int -> int- add2 3;val it = 5 : int- add2 10;val it = 12 : int
Advantages: ExampleAdvantages: ExampleLike the previous quicksortBut now, the comparison function
is the first, curried parameter
Chapter Nine Modern Programming Languages 30
- 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
Multiple Curried Multiple Curried ParametersParametersCurrying generalizes to any
number of parameters
Chapter Nine Modern Programming Languages 31
- 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
Notation For CurryingNotation For CurryingThere is a much simpler notation for
currying (on the next slide)The long notation we have used so far
makes the little intermediate anonymous functions explicit:
But as long as you understand how it works, the simpler notation is much easier to read and write…
Chapter Nine Modern Programming Languages 32
fun g a = fn b => fn c => a+b+c;
Easier Notation for Easier Notation for CurryingCurrying
Instead of writing:fun f a = fn b => a+b;
We can just write:fun f a b = a+b;
This generalizes for any number of curried arguments
Chapter Nine Modern Programming Languages 33
- fun f a b c d = a+b+c+d;val f = fn : int -> int -> int -> int -> int
std::bind in C++0xstd::bind in C++0x
Chapter Nine Modern Programming Languages 34
Function CompositionFunction CompositionUsed frequently in mathematics
and functional programmingDefine a new function as a
sequential application of one or more other functions:◦h(x) = f(g(x))◦Can be written h = f ੦ g◦ML uses the letter ‘o’
g ’s output type must be f ’s input type
Chapter Nine Modern Programming Languages 35
Function CompositionFunction CompositionExample 1Example 1
Chapter Nine Modern Programming Languages 36
- fun f x = x + 2;val f = fn : int -> int- fun g x = 3 * x;val g = fn : int -> int- fun h x = f (g x);val h = fn : int -> int- f (g 5); val it = 17 : int- h 5;val it = 17 : int
Function CompositionFunction CompositionExample 2Example 2
Chapter Nine Modern Programming Languages 37
- f o g;val it = fn : int -> int-val h = f o g; (* or fun h x = (f o g) x; *) val h = fn : int -> int- h 5;val it = 17 : int
Function CompositionFunction CompositionExample 3Example 3
Chapter Nine Modern Programming Languages 38
- map;val it = fn : ('a -> 'b) -> 'a list -> 'b list- map ord;val it = fn : char list -> int list- explode;val it = fn : string -> char list- val a = map ord o explode;val a = fn : string -> int list- a "hello";val it = [104,101,108,108,111] : int list
Strange BehaviorStrange Behavior
Chapter Nine Modern Programming Languages 39
- hd;val it = fn : 'a list -> 'a- tl;val it = fn : 'a list -> 'a list- hd o tl;stdIn:32.1-32.8 Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...)val it = fn : ?.X1 list -> ?.X1
MLML’’s Value Restrictions Value RestrictionA “dark corner”
◦Has to do with making imperative programming as safe as possible in ML
Expressions at the “top level” must be one of the following:◦values (no further evaluation pending)
5 is a value; 2+3 is not
◦monomorphic (no type variables)See next slide
Chapter Nine Modern Programming Languages 40
Value Restriction Value Restriction WorkaroundWorkaround
Chapter Nine Modern Programming Languages 41
- val f = map length;stdIn:18.5-18.19 Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...)val f = fn : ?.X1 list list -> int list- fun f x = map length x; (*In f: not at top-level *)val f = fn : 'a list list -> int list- f [[1,2],[3,4,5]];val it = [2,3] : int list- f nil;val it = [] : int list
OutlineOutlineMore pattern matchingFunction values and anonymous
functionsHigher-order functions and
curryingPredefined higher-order functions
Chapter Nine Modern Programming Languages 42
Predefined Higher-Order Predefined Higher-Order FunctionsFunctionsWe will use three important
predefined higher-order functions:◦ map◦ foldr◦ foldl
Actually, foldr and foldl are very similar, as you might guess from the names
Chapter Nine Modern Programming Languages 43
The The mapmap Function Function
Used to apply a function to every element of a list, and collect a list of results
Chapter Nine Modern Programming Languages 44
- 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
The The mapmap Function Is Function Is CurriedCurried
Chapter Nine Modern Programming Languages 45
- map;val it = fn : ('a -> 'b) -> 'a list -> 'b list- val f = map (op +);val f = fn : (int * int) list -> int list- f [(1,2),(3,4)];val it = [3,7] : int list
Implementing Implementing mapmap
Chapter Nine Modern Programming Languages 46
- fun map f nil = nil
= | map f (x::xs) = f x :: map f xs;
val map = fn : ('a -> 'b) -> 'a list -> 'b list
- map (op ~) [1,2,3];
val it = [~1,~2,~3] : int list
Implementing Implementing mapmapTake 2Take 2
Chapter Nine Modern Programming Languages 47
- fun map f nil = nil
= | map f (x::xs) = (op ::)(f x, map f xs);
val map = fn : ('a -> 'b) -> 'a list -> 'b list
- map (op ~) [1,2,3];
val it = [~1,~2,~3] : int list
A Common PatternA Common PatternConsider adding list elements:fun sum nil = 0| sum (h::t) = (op +)(h,sum t);computes x1 + (x2 + (x3 +… (xn + 0)…))
Or multiplying them:fun mult nil = 1| mult(h::t) = (op * )(h,mult t);computes x1 * (x2 * (x3 *… (xn * 1)…))
The pattern:fun newf nil = c| newf(h::t) = f(h,newf t);computes f(x1,f(x2,f(x3,… f(xn,c)…))
Chapter Nine Modern Programming Languages 48
Capturing the PatternCapturing the PatternSee reduce.smlSee reduce.sml
Make the accumulating function (f) and the initial value (c) parameters to a higher-order function
We’ll call the generic function reduce:
fun reduce _ c nil = c
| reduce f c (h::t) = f(h, reduce f c t);
Chapter Nine Modern Programming Languages 49
What What reducereduce Computes ComputesA DerivationA Derivation
Then reduce f c [x1,x2,…,xn] =f(x1,reduce f c [x2,…,xn]) =f(x1,f(x2,reduce f c [x3,…,xn])) … =f(x1,f(x2, …f(xn,reduce f c nil) …))
f(x1,f(x2, …f(xn,c) …))Note that this evaluates inside-out
◦So the last list element is processed first◦And the “first shall be last” :-)
See also: reduce.py
Chapter Nine Modern Programming Languages 50
Using Using reducereduce to Create to Create FunctionsFunctionsval sum = reduce (op +) 0;
val mult = reduce (op * ) 1;
val anytrue =reduce (fn (x,r) => x orelse r) false;
val alltrue =reduce (fn (x,r) => x andalso r) true;
fun append a b = reduce (op ::) b a;
Chapter Nine Modern Programming Languages 51
Implementing Implementing mapmap with with reducereduce
fun map f =reduce (fn (x, sofar) => (f x)::sofar) nil;
Chapter Nine Modern Programming Languages 52
The The foldrfoldr Function FunctionIn ML In ML reducereduce is called is called foldrfoldr
Chapter Nine Modern Programming Languages 53
- 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
Zipping ListsZipping ListsSuppose you have a pair of
compatible lists◦same type and length◦you can “zip them”
fun zip (nil,nil) = nil
| zip (x::xs, y::ys) = (x,y)::zip (xs,ys);- zip ([1,2],[3,4]);
val it = [(1,3),(2,4)] : (int * int) list
Chapter Nine Modern Programming Languages 54
Unzipping ListsUnzipping Lists
Chapter Nine Modern Programming Languages 55
fun unzip nil = (nil,nil)| unzip ((x,y)::rest) = let val (xs,ys) = unzip rest in (x::xs, y::ys) end;
- unzip [(1,3),(2,4)];val it = ([1,2],[3,4]) : int list * int list
How would you write unzip with foldr?
The The foldlfoldl Function Function
Used to combine all the elements of a list, left-to-rightSame results as foldr in some cases
Chapter Nine Modern Programming Languages 56
- foldl (op +) 0 [1,2,3,4];val it = 10 : int- foldl (op * ) 1 [1,2,3,4];val it = 24 : int
The The foldlfoldl Function Function
It takes a function f, a starting value c, and a list x = [x1, …, xn] and computes:
So foldl (op +) 0 [1,2,3,4] evaluates as 4+(3+(2+(1+0)))=10
Remember, foldr did 1+(2+(3+(4+0)))=10
Chapter Nine Modern Programming Languages 57
cxfxfxfxf nn ,,,, 121
The The foldlfoldl Function Functionfoldl starts at the left, foldr starts at the right
Difference does not matter when the function is commutative, like + and *
For other operations, it does matter
Chapter Nine Modern Programming Languages 58
- 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
std::accumulate in C++std::accumulate in C++
Chapter Nine Modern Programming Languages 59
QueryQueryWhat happens if you replace foldr with foldl in the definition of unzip?
How about append?How would you write reverse
using foldl?
Chapter Nine Modern Programming Languages 60
““FunFun”” for the Enterprising for the Enterprising StudentStudentReview remove.smlWrite functions using that
remove:◦all occurrences of a value from a list◦all duplicates◦curried and non-curried versions
Chapter Nine Modern Programming Languages 61