(* CSE 341, Spring 2011 *)
(* Lecture 5:
pattern-matching (including lists, options, tuples, and records)
functions take 1 argument
tail recursion
accumulators *)
(* defines trees where leaves are constants holding integers and
internal nodes are negations with one child or additions with
two children *)
datatype arith_exp = Constant of int
| Negate of arith_exp
| Add of arith_exp * arith_exp
val test_exp = Add (Constant 19, Negate (Constant 4))
fun eval e =
case e of
Constant i => i
| Negate e2 => ~ (eval e2)
| Add(e1,e2) => (eval e1) + (eval e2)
val fifteen = eval test_exp
fun max_constant e =
case e of
Constant i => i
| Negate e2 => max_constant e2
| Add(e1,e2) => let val m1 = max_constant e1
val m2 = max_constant e2
in
if m1 > m2 then m1 else m2
end
datatype my_int_list = Empty
| Cons of int * my_int_list
val one_two_three = Cons(1,Cons(2,Cons(3,Empty)))
fun append_mylist (l1,l2) = (* a local function could avoid passing l2 *)
case l1 of
Empty => l2
| Cons(hd,tl) => Cons(hd, append_mylist(tl,l2))
(* lists and options are just datatypes -- use patterns
null, hd, and tl functions are so last week -- and for next week, but
used as arguments to other functions
*)
fun inc_or_zero intoption =
case intoption of
NONE => 0
| SOME i => i+1
fun sum_list intlist =
case intlist of
[] => 0
| head::tail => head + sum_list tail
fun append (l1,l2) =
case l1 of
[] => l2
| head::tail => head :: append(tail,l2)
(* can pattern-match each-of types too -- use patterns *)
fun sum_triple triple =
case triple of
(x,y,z) => z + y + x
fun sum_triple_better triple =
let val (x,y,z) = triple
in
x + y + z (* what is the type if we do z + y + z ? *)
end
fun sum_triple_best (x,y,z) =
x + y + z
(* so it turns out:
(a) every function in ML takes exactly one argument!!!
(note: () is also a pattern)
(b) val bindings actually allow patterns, variables are just one
kind of pattern (more on this Friday)
*)
fun rotate_left (x,y,z) = (y,z,x)
(* you can't do this in Java *)
fun rotate_right triple = rotate_left(rotate_left triple)
(* patterns for records are very much the same -- need this in one place
in homework 2 since #foo is "forbidden" *)
fun sum_stooges triple =
case triple of
{larry=x,curly=y,moe=z} => z + y + x
fun sum_stooges_better triple =
let val {larry=x,curly=y,moe=z} = triple
in
x + y + z
end
fun sum_triple_best {larry=x,curly=y,moe=z} =
x + y + z
(* oh, and if expressions are just syntactic sugar for case *)
(* pre-defined for you: datatype bool = true | false *)
fun bool_to_int_1 b =
case b of (* totally works but inferior style *)
true => 1
| false => 0
fun bool_to_int_2 b =
if b (* means _exactly_ what the case-expression above means *)
then 1
else 0
(* recursion and using accumulators *)
fun fact n =
case n of
0 => 1
| n => n * fact(n-1)
(*
fact 4
=> 4 * fact(3)
=> 4 * 3 * fact(2)
=> 4 * 3 * 2 * fact(1)
=> 4 * 3 * 2 * 1 * fact(0)
=> 4 * 3 * 2 * 1 * 1
=> ..
=> 24
*)
fun factt n =
let fun aux(n,acc) =
case n of
0 => acc
| n => aux(n-1, n*acc)
in
aux(n,1)
end
(*
factt 4
=> aux(4,1)
=> aux(3,4)
=> aux(2,12)
=> aux(1,24)
=> aux(0,24)
=> 24
*)
fun rev(lst: int list) =
case lst of
[] => []
| x::xs => rev(xs) @ [x]
fun revt(lst: int list) =
let fun aux(lst,acc) =
case lst of
[] => acc
| x::xs => aux(xs, x::acc)
in
aux(lst,[])
end
fun sum_mylist_1 lst =
case lst of
Empty => 0
| Cons(i,lst1) => i + sum_mylist_1 lst1
fun sum_mylist_2 lst =
let fun f (lst,acc) =
case lst of
Empty => acc
| Cons(i,lst1) => f(lst1,i+acc)
in
f(lst,0)
end
(* tail recursion usually possible for list functions, but not so much
for tree functions (without much fancier rewriting that give up the
performance advantage) *)
fun max_constant2 e =
let fun f (e,acc) =
case e of
Constant i => if i > acc then i else acc
| Negate e2 => f(e2,acc)
| Add(e1,e2) =>
(* <= 1 of the recursive calls can be in tail position *)
(* let val m1 = f(e1,acc)
val m2 = f(e2,m1)
in
m2
end
*)
f(e2,f(e1,acc)) (* 1 f is in tail-position, 1 f is not *)
in
f(e,valOf Int.minInt) (* not my favorite style, but okay here *)
end