310 likes | 476 Views
ML Again ( Chapter 7). Patterns Local variable definitions Two sorting examples. The implementation using functional languages is closer to the concepts of the solutions. Both n and ( a,b ) are patterns. fun f n = n*n; fun g (a,b) = a*b;. n is a pattern of integers;
E N D
ML Again ( Chapter 7) • Patterns • Local variable definitions • Two sorting examples The implementation using functional languages is closer to the concepts of the solutions. IT 327
Both n and (a,b) are patterns. • fun f n = n*n; • fun g (a,b) = a*b; n is a pattern of integers; (a, b) is a pattern of 2-tuple of integers. For match and bind(most of the time) f 2; (matched) f (2,3); (not matched) g 2; (not matched) g (2,3); (matched) IT 327
Underscore is a Pattern - fun f _ = "yes"; val f = fn : 'a -> string - f 34.5; val it = "yes" : string - f []; val it = "yes" : string • The underscore matches anything, but does not bind it to a variable fun f x = "yes"; IT 327
Constants are Patterns - fun f 0 = "yes"; Warning: match nonexhaustive 0 => ... val f = fn : int -> string - f 0; val it = "yes" : string • Any constant of an equality type can be used as a pattern • But not:fun f 0.0 = "yes"; IT 327
Non-Exhaustive Match • “match non-exhaustive” warning • Meaning: f was defined using a pattern that didn’t cover all the domain type (int) • So you may get runtime errors like this: - f 0; val it = "yes" : string - f 1; uncaught exception nonexhaustive match failure IT 327
Lists Of Patterns As Patterns - fun f [a,_] = a; Warning: match nonexhaustive a :: _ :: nil => ... val f = fn : 'a list -> 'a - f [#"f",#"g"]; val it = #"f" : char • You can use a list of patterns as a pattern • This example matches any list of length 2 • It treats a and _ as sub-patterns, binding a to the first list element Why not 1? IT 327
Cons Of Patterns As A Pattern - fun f (x::xs) = x; Warning: match nonexhaustive x :: xs => ... val f = fn : 'a list -> 'a - f [1,2,3]; val it = 1 : int • You can use a cons of patterns as a pattern • x::xs matches any non-empty list, and binds x to the head and xs to the tail • Parentheses around x::xs are for precedence IT 327
Little Summary • A variable is a pattern that matches anything, and binds to it • A _ is a pattern that matches anything • A constant (of an equality type) is a pattern that matches only that constant • A tuple of patterns is a pattern that matches any tuple of the right size, whose contents match the sub-patterns • A list of patterns is a pattern that matches any list of the right size, whose contents match the sub-patterns • A cons (::) of patterns is a pattern that matches any non-empty list whose head and tail match the sub-patterns IT 327
Multiple Patterns for Functions - fun f 0 = "zero"= | f 1 = "one"; Warning: match nonexhaustive 0 => ... 1 => ... val f = fn : int -> string; - f 1; val it = "one" : string • You can define a function by listing alternate patterns IT 327
Syntax <fun-def> ::= fun <fun-bodies> ; <fun-bodies> ::= <fun-body> <fun-bodies> ::= <fun-body> | <fun-bodies> <fun-body> ::= <fun-name> <pattern> = <expression> • To list alternate patterns for a function • You must repeat the same function name in each alternative We just hit the limitation of CFG IT 327
Overlapping Patterns - fun f 0 = "zero"= | f _ = "non-zero"; val f = fn : int -> string; - f 0; val it = "zero" : string - f 34;val it = "non-zero" : string • Patterns may overlap • ML uses the first match for a given argument IT 327
Pattern-Matching Style • Two alternatives: fun f 0 = "zero" | f _ = "non-zero"; fun f n = if n = 0 then "zero" else "non-zero"; • Pattern-matching style is preferred in ML • shorter and more legible IT 327
Pattern-Matching Examples fun fact n = if n = 0 then 1 else n * fact(n-1); fun fact 0 = 1 | fact n = n * fact(n-1); fun reverse L = if null L then nil else reverse(tl L) @ [hd L]; fun reverse nil = nil | reverse (first::rest) = reverse rest@[first]; IT 327
A Restriction How to explain this? fun f (a,a) = … | f (a,b) = … This is not legal, but why? fun f (a,b) = if (a=b) then … else … IT 327
Patterns in val - val (a,b) = (1,2.3); val a = 1 : int val b = 2.3 : real - val a::b = [1,2,3,4,5]; Warning: binding not exhaustive a :: b = ... val a = 1 : int val b = [2,3,4,5] : int list IT 327
Local Variable Definitions <let-exp> ::= let <definitions> in <expression> end - let val x = 1 val y = 2 in x+y end; val it = 3 : int; - x; Error: unbound variable or constructor: x let val x = 1 val y = 2 in x+y End; This alone is not very useful! We can put it into a function definition IT 327
Long Expressions with let fun days2ms days = days*24.0*60.0*60.0*1000.0; fun days2ms days = let val hours = days * 24.0 val minutes = hours * 60.0 val seconds = minutes * 60.0 in seconds * 1000.0 end; IT 327
You should have known this well. (if you have taken 279) Merge Sort Merge two sorted lists 3 5 7 8 1 2 6 IT 327
Merge Sort IT 327
How to split In ML, it’s a bit difficult to do this We have hd,tl and :: 7 3,8,2,1,6 5 Recursively IT 327
halve fun halve nil = (nil, nil) | halve [a] = ([a], nil) | halve (a::b::cs) = let val (x, y) = halvecs in (a::x, b::y) end; • -halve [1]; • val it = ([1],[]) : int list * int list • halve [1,2]; • val it = ([1],[2]) : int list * int list • - halve [1,2,3,4,5,6]; • val it = ([1,3,5],[2,4,6]) : int list * int list IT 327
Recursively merge 7 xs 5 ys 5 fun merge (nil, ys) = ys| merge (xs, nil) = xs| merge (x::xs, y::ys) = if (x < y) then x :: merge(xs, y::ys) else y :: merge(x::xs, ys); IT 327
Merge Sort fun mergeSort nil = nil | mergeSort [a] = [a] | mergeSort theList =let val (x, y) = halve theList in merge(mergeSort x, mergeSort y) end; • int list -> int list, IT 327
Merge Sort At Work These two functions can be defined locally. - fun mergeSort nil = nil = | mergeSort [a] = [a] = | mergeSort theList = = let = val (x, y) = halve theList = in = merge(mergeSort x, mergeSort y) = end; val mergeSort = fn : int list -> int list - mergeSort [4,3,2,1]; val it = [1,2,3,4] : int list - mergeSort [4,2,3,1,5,3,6]; val it = [1,2,3,3,4,5,6] : int list here IT 327
fun mergeSort nil = nil| mergeSort [e] = [e]| mergeSort theList =let(* fun and var defined locally *) fun halve nil = (nil, nil) | halve [a] = ([a], nil) | halve (a::b::cs) = let val (x, y) = halve cs in (a::x, b::y) end; fun merge (nil, ys) = ys | merge (xs, nil) = xs | merge (x::xs, y::ys) = if (x < y) then x :: merge(xs, y::ys) else y :: merge(x::xs, ys); val (x, y) = halve theListin merge(mergeSort x, mergeSort y)end; IT 327
Quick Sort < < < < < < < < < < < < < < < < < < < < < < < < < < IT 327
pivot Quick Sort 4 3 2 16 14 8 Using imperative PL’s, we have to explicitly implement these iterations. IT 327
private void SwapTwo(int A[], int i, int j){ int temp = A[i]; A[i]=A[j]; A[j]=temp; } public void QuickSort(int A[], int h, int t){ if (h == t) return; int i=h+1, j=t; if (i==j) { if (A[h] > A[i]) SwapTwo(A,h,i); return; } while (i < j) { while (A[h] >= A[i] && i < t) i++; while (A[h] <= A[j] && j > h) j--; if (i < j) SwapTwo(A,i++,j--); if (i==j && A[h]>A[i]) SwapTwo(A,h,i); } QuickSort(A,h,i-1); QuickSort(A,i,t); } Quick Sort in Java We have to explicitly implement these iterations. IT 327
Quick Sort in ML fun split nil = (nil,nil) | split [a] = ([a],nil) | split (a::b::tail) = let val (x,y) = split (a::tail) 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; pivot This is more conceptual to the understanding of the problem Anything wrong? How about split [2,2] ? 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; Force to split when there are two elements. IT 327
Homework 3 Chapter 7. Exercise: 2, 3, 5, 7, 8, 9, 10 Due Oct 17. IT 327