(* Lecture 4: Higher-Order Functions, Datatypes *) (* Author: Frank Pfenning *) (* Higher-Order Functions *) (* The following are equivalent ways of defining the reciprocal of a function val reciprocal : (real -> real) -> (real -> real) usually written as val reciprocal : (real -> real) -> real -> real. *) fun reciprocal (f:real -> real) = fn (x:real) => 1.0/f(x); fun reciprocal (f:real -> real) (x:real) = 1.0/f(x); (* find0 (eps,f,a,b) where f(a)<0, f(b)>0, f continuous on [a,b] *) (* eps > 0 *) (* return x if we find x such that f(x) = 0 or |b-a| < eps *) fun find0 (eps:real, f:real -> real, a:real, b:real) = let val x = (a+b)/2.0 val y = f(x) in if y = 0.0 orelse Real.abs(b-a) < eps then x else if y > 0.0 then find0 (eps,f,a,x) else find0 (eps,f,x,b) end; (* exception Domain to be raised if a function is called incorrectly *) exception Domain; (* findZero (eps,f,a,b) is like find0, but checks invariants (possibly swapping a and b) and raises exception Domain if they are not satisfied. *) fun findZero (eps:real, f:real -> real, a:real, b:real) = if eps <= 0.0 then raise Domain else if f(a) < 0.0 andalso f(b) > 0.0 then find0 (eps,f,a,b) else if f(a) > 0.0 andalso f(b) < 0.0 then find0 (eps,f,b,a) else raise Domain; (* We can use this idea to get a curried implementation of findZero which can be used to get more specialized functions *) (* findZero eps f (a,b) is like find0, but checks invariants (possibly swapping a and b) and raises exception Domain if they are not satisfied. This version has curried arguments. val findZero : (real * real) -> (real -> real) -> (real * real) -> real This is the same type as val findZero : (real * real) -> ((real -> real) -> ((real * real) -> real)) *) fun findZero (eps:real) (f:real -> real) (a:real, b:real) = if eps <= 0.0 then raise Domain else if f(a) < 0.0 andalso f(b) > 0.0 then find0 (eps,f,a,b) else if f(a) > 0.0 andalso f(b) < 0.0 then find0 (eps,f,b,a) else raise Domain; val findZero_6 : (real -> real) -> (real * real) -> real = findZero 1E~6; val findZero_6a : (real * real) -> real = findZero_6 (fn (x:real) => x * x - 2.0); val cs0 : real = findZero_6a(~2.0,0.0); val cs1 : real = findZero_6a(0.0,2.0); (* Datatypes *) datatype suit = Clubs | Spades | Hearts | Diamonds (* toString (s:suit) = "name (in German)" *) fun toString (Clubs) = "Kreuz" | toString (Spades) = "Pik" | toString (Hearts) = "Herz" | toString (Diamonds) = "Karo"; (* dominates(s1,s2) = true iff suit s1 is higher than s2 (in Germany) *) fun dominates (Clubs, Spades) = true | dominates (Clubs, Hearts) = true | dominates (Clubs, Diamonds) = true | dominates (Spades, Hearts) = true | dominates (Spades, Diamonds) = true | dominates (Hearts, Diamonds) = true | dominates (_, _) = false; (* rank of cards in a German deck *) datatype rank = Seven | Eight | Nine | Ten | Jack | Queen | King | Ace; (* value (r:rank) = numerical value of rank for comparison *) fun value (Seven) = 7 | value (Eight) = 8 | value (Nine) = 9 | value (Ten) = 10 | value (Jack) = 11 | value (Queen) = 12 | value (King) = 13 | value (Ace) = 14; (* beats(r1,r2) = true iff rank r1 is higher than r2 *) fun beats (r1:rank, r2:rank) = value(r1) > value(r2); datatype card = Card of suit * rank; (* higher(card1, card2) is card1 is higher than card2 *) fun higher (Card(s1,r1),Card(s2,r2)) = dominates (s1, s2) orelse ((s1 = s2) andalso beats(r1, r2)); (* suitOf(card) = suit of the card *) fun suitOf (Card(s,r)) = s; (* hand a list of cards *) (* representation invariant: no card is duplicated *) datatype hand = Empty | Hold of card * hand; (* collectSuit (s:suit, h:hand) = hand consisting of cards in h of suit s *) fun collectSuit (s, Empty) = Empty | collectSuit (s, Hold(Card(s',r),h)) = if s = s' then Hold (Card(s',r), collectSuit (s, h)) else collectSuit (s, h); val hand1 = Hold(Card(Spades,Ace), Hold(Card(Diamonds,Ten), Hold(Card(Clubs,Seven), Hold(Card(Spades,Queen), Hold(Card(Clubs,Eight), Empty))))); val spades1 = collectSuit (Spades, hand1);