week3主要内容:
自定义类型、语法糖、case表达式、多态类型、巧妙嵌套、尾递归。
以下为示例代码。
(*syntactic sugar*)
val a_pair = (3+1, 4+2);
val a_record = {second = 6, first = 3+1};
(*this is a pair, why?*)
val another_pair = {2 = 5, 1 = 6};
val x = {3 = "hi", 1 = true};
val y = {3 = "hi", 1 = true, 2 = 5};
(*there is no such thing as tuple in ML, only records!*)
(*tuples are just syntactic sugar for records*)
(*datatype bindings*)
datatype mytype = Twoints of int*int
|Str of string
|Pizza
(*Twoints and Str are funs, Pizza is a value*)
val a = Str "hi"
val b = Str (*string -> mytype *)
val c = Pizza
val d = Twoints(3,7)
val e = a;
(*datatype bindings using aspects:*)
(*1. check what val it is*)
(*2. extract the data*)
(*case expressions*)
(*mytype -> int*)
fun f(x:mytype) =
case x of
Pizza => 3
|Str s => String.size s
|Twoints(i1, i2) => i1+i2;
f Pizza;
f(Str "hi");
(*f("hi");*) (*wrong, not the type expects*)
f(Twoints(3,6));
(*why this is better*)
(* funs must contain all type branches, or it will warn. you wont forget*)
(*useful datatypes*)
datatype id = StudentNum of int
| Name of string*(string option)*string
(*expression trees*)
datatype exp = Constant of int
|Negate of exp
|Add of exp * exp
|MUL of exp * exp
;
(*recurtion*)
fun eval(e:exp) =
case e of
Constant i => i
|Negate e2 => ~(eval e2)
|Add(e1, e2) => (eval e1) + (eval e2)
|MUL(e1, e2) => (eval e1) * (eval e2)
;
val exam_exp:exp = Add(Constant(10+9), Negate(Constant 4));
val exam_ans:int = eval exam_exp;
(*type synnonyms*)
(*create another name for the same type*)
datatype suit = club|Diamond|Heart|Spade
datatype rank = Jack|Queen|King|Ace|Num of int
type card = suit*rank
fun is_Queen_of_Spades(c:card) =
#1 c = Spade andalso #2 c = Queen
fun is_Queen_of_Spades2(c:card) =
case c of
(Spade, Queen) => true
| _ =>false
val c1 :card = (Diamond, Ace)
val c2 : suit*rank = (Heart, Ace)
val c3 = (Spade, Ace)
is_Queen_of_Spades c1;
is_Queen_of_Spades c2;
is_Queen_of_Spades c3;
is_Queen_of_Spades2 c1;
is_Queen_of_Spades2 c2;
is_Queen_of_Spades2 c3;
(*another example*)
(*
datatype exp = Constant of int
|Negate of exp
|Add of exp * exp
|MUL of exp * exp
*)
fun max_constant e =
case e of
Constant i => i
|Negate e2 => max_constant e2
|Add(e1, e2) => if max_constant e1 > max_constant e2
then max_constant e1
else max_constant e2
|MUL(e1, e2) => if max_constant e1 > max_constant e2
then max_constant e1
else max_constant e2
val test_exp:exp = Add(Constant(10+9), Negate(Constant 4))
val nineteen = max_constant test_exp
(*to be concise*)
fun max_constant2 e =
let fun max_of_two(e1,e2) =
let val m1 = max_constant e1
val m2 = max_constant e2
in
if m1 > m2
then m1
else m2
(*or Int.max(m1, m2)*)
end
in
case e of
Constant i => i
|Negate e2 => max_constant e2
|Add(e1, e2) => max_of_two(e1, e2)
|MUL(e1, e2) => max_of_two(e1, e2)
end
(*to be concise*)
fun max_constant3 e =
case e of
Constant i => i
|Negate e2 => max_constant e2
|Add(e1, e2) => Int.max(max_constant e1, max_constant e2)
|MUL(e1, e2) => Int.max(max_constant e1, max_constant e2)
(*list and option datatypes*)
datatype my_int_list = Empty
| Cons of int*my_int_list
fun append_my_list(xs, ys) =
case xs of
Empty => ys
|Cons(x, xs') => Cons(x, append_my_list(xs', ys))
fun inc_or_zero intr =
case intr of
NONE => 0
|SOME i => i+1
fun sum_list xs =
case xs of
[] => 0
| x::xs' => x +sum_list xs'
fun append (xs, ys) =
case xs of
[] => ys
|x::xs' => x:: append(xs', ys)
(*polymophic datatypes*)(*多态*)
datatype 'a option = NONE | SOME of 'a
datatype 'a mylist = Empty |Cons of 'a *'a mylist
datatype ('a, 'b) tree =
Node of 'a * ('a, 'b) tree * ('a, 'b) tree
|Leaf of 'b
(*one or more type var before datatype name*)
fun sum_tree tr =
case tr of
Leaf i => i
|Node(i, lft, rgt) => i+ sum_tree lft+ sum_tree rgt
fun sum_leaves tr =
case tr of
Leaf i => 1
|Node(i, lft, rgt) => sum_leaves lft + sum_leaves rgt
val tree_test = Node(3, Node(2, Leaf 1, Leaf 4), Leaf 7);
sum_tree tree_test;
sum_leaves tree_test;
(*eachof pattern matching*)
(*argments:实参*)
fun sum_triple triple =
case triple of
(x, y, z) =>x+y+z
(*better style*)
fun sum_triple2 triple =
let val(x,y,z) = triple
in
x+y+z
end
;
(*great style*)
fun sum_triple3(x,y,z) =x+y+z ;
sum_triple3(1,2,3)
(*So how can ML tell which im trying to do?*)
(*how does it know to pass in on triple and pattern match against it,
instead of passing in three arguments?*)
(*ans: In ML, every function takes and returns exactly one argument*)
fun rotate_left(x,y,z) = (y,z,x);
rotate_left(1,2,3);
fun rotate_right(x,y,z) = rotate_left(rotate_left(x,y,z));
rotate_right(1,2,3);
(*type inference*)
fun sumtriple1(x,y,z) = x+y+z;
fun sumtriple2(triple: int*int*int) = #1 triple + #2 triple + #3 triple;
(*get int*'a*int -> int , 'a can be anytype *)
(*called unexpected polymorphism*)
fun partialsum(x,y,z) = x+z;
(*polymophic types and equality types*)
(* fun append (xs, ys) =
case xs of
[] => ys
|x::xs' => x:: append(xs', ys) *)
(*it has to be the esame type*)
(*val not_ok = append([1,2], ["programming", "languages"]);*)
(*two quotes before a type: equality types*)
(* fn: ''a *''a -> string *)
fun same_thing(x, y) =
if x = y
then "yes"
else "no"
(*same_thing(1,"ji");*)
(*nested patterns*)(*嵌套*)
(*don't do this*)
exception ListLengthMismatch
(*'a lsit *'b list * 'c list -> ('a*'b*'c) list*)
fun old_zip(l1, l2, l3) =
if null l1 andalso null l2 andalso null l3
then []
else if null l1 orelse null l2 orelse null l3
then raise ListLengthMismatch
else (hd l1, hd l2, hd l3) :: old_zip(tl l1, tl l2, tl l3)
;
old_zip([],[],[]);
old_zip([1], [2], ["hi"]);
(*do this*)
fun zip list_triple =
case list_triple of
([],[],[]) => []
| (hd1 :: tl1, hd2 ::tl2, hd3:: tl3) => (hd1, hd2, hd3) :: zip(tl1, tl2, tl3)
| _ => raise ListLengthMismatch
;
(*妙啊*)
fun unzip lst =
case lst of
[] => ([],[],[])
| (a, b, c) :: tail =>
let val (l1, l2 ,l3) = unzip tail
in (a:: l1, b:: l2, c:: l3)
end
;
val testzip = zip([],[],[]);
val testzip2 = zip([1,2,3],[4,5,6],["a","b","c"]);
val testunzip = unzip([(1,2,3),(4,5,6)]);
(*more nested patterns*)
fun nondecreasing xs =
case xs of
[] => true
| _ :: [] => true
| head:: (neck :: rest) => head <= neck andalso nondecreasing (neck:: rest)
;
val test_nonde = nondecreasing ([1,2]);
datatype sgn = P|N|Z
fun multsign (x1, x2) =
let fun sign x =
if x = 0
then Z
else if x>0
then P
else N
in
case (sign x1, sign x2) of
(Z, _) => Z
|( _, Z) => Z
|(P, P) => P
|(N, N) => P
|_ => N
end
;
val test_muls = multsign(3, ~3);
(*nested patterns can lead to very concise code*)
(*wildcards are good styles*)
(*nested patterns precisely*)
(*pattern a::b::c::d matches all lists with >=3 ele*)
(*pattern a::b::c::[] mathes all lists with 3 ele*)
(*function patterns*)
datatype exp = Constant of int
|Negate of exp
|Add of exp * exp
|MUL of exp * exp
;
fun eval e =
case e of
Constant i => i
|Negate e2 => ~(eval e2)
|Add(e1, e2) => (eval e1) + (eval e2)
|MUL(e1, e2) => (eval e1) * (eval e2)
;
(*its just syntactic sugar for case expression*)
fun eval2 (Constant i ) = i
|eval2 (Negate e2) = ~(eval2 e2)
|eval2 (Add (e1, e2)) = (eval2 e1) + (eval2 e2)
|eval2 (MUL (e1, e2)) = (eval2 e1) * (eval2 e2)
;
(*int list *int list -> int list*)
fun append2 ([], ys) = ys
|append2 (x::xs, ys) = x::append2(xs, ys)
;
val test_eval2 = eval2(Constant 4);
val test_append2 = append2([8],[6]);
(*exceptions*)
fun hd xs =
case xs of
[] => raise List.Empty
|x::_ => x
;
hd [];
exception MyOtherException of int * int
raise MyOtherException(3,4);
exception MyUndesirableCondition;
(*int list *exc -> int*)
fun maxlist(xs, ex) =
case xs of
[] =>raise ex
|x::[] => x
|x::xs' =>Int.max(x, maxlist(xs', ex))
;
val test_max = maxlist([1,3,6,2], MyUndesirableCondition);
(*
e1 handle ex => e2
if e1 evaluates normally, the rest is irrelavent.
but if e1 raises exception listed here, catch the exception and evaluate e2.
if it is not the exception, not handle it and it continues.
with params: e1 handle ex(x, y) =>e2
*)
val catchexc = maxlist([], MyUndesirableCondition)
handle MyUndesirableCondition => 50;
(*declaring an exception makes adds a constructor for type exn*)
(*tail recursion*)
(*weather recusion is effficient or in what situations does it lead to fast code*)
fun fact n =
if n = 0
then 1
else n*fact(n-1)
;
(*with a local helper function ,aux, that takes an extra argument*)
fun fact2 n =
let fun aux(n, acc) =
if n = 0
then acc
else aux(n-1, acc*n)
in
aux(n, 1)
end
;
val x = fact2 3;
(*it is unnessary to keep around a stack-frame just so
it can get a callee's result without ant further evaluation*)
(*the compiler: remove the caller's stack frame before it makes the call,
the call just reuses the same stack space*)
(*so tail recursion is more efficient!*)
(*accumulators*)
fun sum xs =
case xs of
[] => 0
| x:: xs' => x + sum xs'
fun sum2 xs =
let fun aux(xs, acc) =
case xs of
[] => acc
|x::xs' => aux(xs', acc+x)
in aux(xs, 0)
end
val ll = [1,2,3,4];
sum(ll);
sum2(ll);
fun rev xs =
case xs of
[] => []
| x:: xs' => (rev xs') @ [x]
;
fun rev2 xs =
let fun aux(xs, acc) =
case xs of
[] => acc
| x::xs' => aux(xs', x:: acc)
in aux(xs, [])
end
;
rev ll;
rev2 ll;
(*tail recusive :perspective and definition*)
(*there are cases where recursive function cannot be evaluated in a constant amount of trees*)
(*a tail position is there won't be anything to do afterwards*)
error log
- 多重case嵌套要有加括号的习惯,否则有混淆的可能。
- arguments 中有record, 一定要将record的组成成分写出来。比如:
fun similar_names (sll, dup:{first:string,last:string,middle:string}) =
PS. 用不惯emacs的小伙伴可以用vscode. 搜索ML 相关插件。