310 likes | 322 Views
Explore the implementation and usage of patterns and local variable definitions in functional languages like ML. Understand how patterns such as variables, tuples, constants, lists, and cons are utilized. Learn to define functions with multiple patterns and handle overlapping patterns effectively. Discover the concept of local variable definitions with examples.
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 any arguments, and the variable binds to the argument. • A _ is a pattern that matches any arguments. • 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 • You can define a function by listing alternate patterns - fun f 0 = "zero"= | f 1 = "one"; Warning: match nonexhaustive 0 => ... 1 => ... val f = fn : int -> string; - f 1; val it = "one" : string IT 327
Syntax (BNF) • To list alternate patterns for a function • You must repeat the same function name in each alternative <fun-def> ::= fun <fun-bodies> ; <fun-bodies> ::= <fun-body> <fun-bodies> ::= <fun-body> | <fun-bodies> <fun-body> ::= <fun-name> <pattern> = <expression> We just hit the limitation of BNF (CFG) - fun f 0 = "zero"= | f 1 = "one"; 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 ML uses the first match for a given argument IT 327
Pattern-Matching vs if-expression 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) = "same" | f (a,b) = "different"; This is not legal, but why? fun f (a,b) = if (a=b) then "same" else "different"; f (1,2); val it = "different" : string 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 x and y do not exist outside this let-expression 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
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
fun halve nil = (nil, nil) | halve [a] = ([a], nil) | halve (a::b::cs) = let val (x, y) = halvecs in (a::x, b::y) end; halvetype? -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 This is more conceptual to the understanding of the problem 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
Exercise Chapter 7. Exercise: 2, 3, 5, 7, 8, 9, 10 IT 327