Code
(* Dan Grossman, Coursera PL, HW2 Provided Code *)
(* if you use this function to compare two strings (returns true if the same
string), then you avoid several of the functions in problem 1 having
polymorphic types that may be confusing *)
fun same_string(s1 : string, s2 : string) =
s1 = s2
(* put your solutions for problem 1 here *)
fun all_except_option (str : string, xs : string list) =
let
fun isInside (xs : string list) =
case xs of
[] => false
| x::xs' => if same_string(str,x) then true
else isInside(xs')
fun all_except_option_help (xs : string list, ys: string list) =
case xs of
[] => ys
| x::xs' => if same_string(str, x) then all_except_option_help (xs', ys)
else all_except_option_help (xs', ys@[x])
in
case isInside(xs) of
false => NONE
| true => SOME(all_except_option_help(xs, []))
end
fun get_substitutions1 (strlistlist : string list list, s : string ) =
case strlistlist of
[] => []
| strlist::strlistlist' => let
val res = all_except_option (s, strlist)
in
case res of
NONE => get_substitutions1 (strlistlist', s)
| SOME reslist => reslist @ get_substitutions1 (strlistlist', s)
end
fun get_substitutions2 (strlistlist : string list list, s : string ) =
let fun get_substitutions2_helper (strlistlist : string list list, acc : string list) =
case strlistlist of
[] => acc
| strlist::strlistlist' => let
val res = all_except_option (s, strlist)
in
case res of
NONE => get_substitutions2_helper (strlistlist', acc)
| SOME reslist => get_substitutions2_helper (strlistlist', acc@reslist)
end
in
get_substitutions2_helper (strlistlist, [])
end
fun similar_names (strlistlist : string list list, fullname : {first:string, middle:string, last:string} ) =
let val {first=x, middle=y, last=z} = fullname
val firstnamelist = get_substitutions2 (strlistlist, x)
fun similar_names_helper (firstnamelist : string list, fullnamelist : {first:string, last:string, middle:string} list) =
case firstnamelist of
[] => fullnamelist
| firstname::firstnamelist' => similar_names_helper(firstnamelist', fullnamelist @ [ {first = firstname, last = z, middle = y}])
in
similar_names_helper(firstnamelist, [{first = x, last = z, middle = y}])
end
(* you may assume that Num is always used with values 2, 3, ..., 9
though it will not really come up *)
datatype suit = Clubs | Diamonds | Hearts | Spades
datatype rank = Jack | Queen | King | Ace | Num of int
type card = suit * rank
datatype color = Red | Black
datatype move = Discard of card | Draw
exception IllegalMove
(* put your solutions for problem 2 here *)
fun card_color (c : card ) =
let val (s, r) = c
in
case s of
Spades => Black
| Clubs => Black
| Diamonds => Red
| Hearts => Red
end
fun card_value (c : card ) =
let val (s, r) = c
in
case r of
Ace => 11
| Jack => 10
| Queen => 10
| King => 10
| Num(num) => num
end
fun remove_card (cs : card list, c: card, e) =
let
fun isInside (xs : card list) =
case xs of
[] => false
| x::xs' => if x = c then true
else isInside(xs')
fun remove_card_helper (xs : card list, result : card list) =
case xs of
[] => result
| x::xs' => if (c = x) then result @ xs' else remove_card_helper(xs', result @ [x])
in
if (isInside(cs)) then remove_card_helper (cs, [])
else raise e
end
fun all_same_color (cs : card list) =
case cs of
[] => true
| _::[] => true
| head :: (neck :: rest) => ((card_color(head) = card_color(neck)) andalso all_same_color(neck :: rest))
fun sum_cards (cs : card list) =
let fun sum_cards_helper (cs : card list, acc : int) =
case cs of
[] => acc
| c::cs' => sum_cards_helper(cs', acc + card_value(c))
in
sum_cards_helper(cs, 0)
end
fun score (cs : card list, goal : int) =
let
val s = sum_cards (cs)
val pScore = if (s > goal) then 3*(s - goal) else (goal - s)
val result = if (all_same_color(cs)) then (pScore div 2) else pScore
in
result
end
fun officiate (cardlist : card list, movelist : move list, goal : int) =
let
fun officiate_helper (cardlist : card list, movelist : move list, heldcards : card list) =
case movelist of
[] => heldcards
| move::movelist' => case move of
Draw => ( case cardlist of
[] => heldcards
| card::cardlist' => if (sum_cards (card :: heldcards) > goal)
then card :: heldcards
else officiate_helper(cardlist', movelist', card :: heldcards)
)
| Discard c => remove_card(heldcards, c, IllegalMove)
in
score(officiate_helper (cardlist, movelist, []), goal)
end
Test Cases
(* Dan Grossman, Coursera PL, HW2 Provided Tests *)
(* These are just two tests for problem 2; you will want more.
Naturally these tests and your tests will use bindings defined
in your solution, in particular the officiate function,
so they will not type-check if officiate is not defined.
*)
fun provided_test1 () = (* correct behavior: raise IllegalMove *)
let val cards = [(Clubs,Jack),(Spades,Num(8))]
val moves = [Draw,Discard(Hearts,Jack)]
in
officiate(cards,moves,42)
end
fun provided_test2 () = (* correct behavior: return 3 *)
let val cards = [(Clubs,Ace),(Spades,Ace),(Clubs,Ace),(Spades,Ace)]
val moves = [Draw,Draw,Draw,Draw,Draw]
in
officiate(cards,moves,42)
end
val all_except_option_test = all_except_option("a", ["b","c","d","e","f"]);
val get_substitutions1_test = get_substitutions2([["Fred","Fredrick"],["Elizabeth","Betty"],["Freddie","Fred","F"]],"Fred");
val get_substitutions1_test = get_substitutions2([["Fred","Fredrick"],["Jeff","Jeffrey"],["Geoff","Jeff","Jeffrey"]],"Jeff");
val similar_names_test = similar_names([["Fred","Fredrick"],["Elizabeth","Betty"],["Freddie","Fred","F"]],{first="Fred", middle="W", last="Smith"});
val card_color_test = card_color((Clubs,Jack));
val card_value_test = card_value((Clubs,Jack));
val cards = [(Clubs,Jack),(Spades,Num(8))]
val remove_card_test = remove_card ([(Clubs,Jack),(Spades,Num(8))], (Spades,Num(7)), IllegalMove);
val all_same_color_test = all_same_color([(Clubs,Jack),(Spades,Num(8))]);
val sum_cards_test = sum_cards([(Clubs,Jack),(Spades,Num(8))]);
val score_test = score([(Clubs,Jack),(Spades,Num(8))], 10);