(* file: optionsMaps.ml author: Bob Muller CS1103 Computer Science I Honors Lecture Notes for meeting 1 of week 7 Topics: 1. Option Types 2. Dictionaries aka Maps 1. Option Types Sometimes a function is called to produce an answer. But there isn't one! We could call failwith but we might also wrap the result in the built-in option type: quotient : int -> int -> int option *) let quotient m n = match n = 0 with | true -> None | false -> Some (m / n) (* isDivOK : int -> int -> string *) let isDivOK m n = match quotient m n with | None -> "Div is NOT OK" | Some q -> "Div is OK, quotient is " ^ (string_of_int q) (* In some programming languages options are called "maybe" types. *) type 'a maybe = Nothing | Just of 'a type veggie = Broccoli | Pepper | Onion (* favorite : string -> veggie maybe Only Bob doesn't like vegetables. *) let favorite name = match name = "Bob" with | true -> Nothing | false -> Just Pepper (* 2. Dictionaries/Maps ************************************************* We often want to relate one thing to another. For example, our contacts apps relate our friends names to their contact info, dictionaries relate words to their definitions. To fix terminology, lets call the "word" a "key" and its definition a "value". There are many things we might want to do with dictionaries but lets focus on 3 main functions working on dictionaries: isEmpty : dictionary -> bool add : key -> value -> dictionary -> dictionary find : key -> dictionary -> value option Two representations, one simple very with a really fast add but a slow find. This is undesirable because in most applications, the find operation is far more common (think credit-card lookups). The second representation has a much faster find at the cost of a slower add. We'll take it! Association Lists -- named by the great John McCarthy, the inventor of LISP the principal precursor of OCaml! *) type ('key, 'value) dictionary = ('key * 'value) list (* An assocation list is a list of key/value pairs. isEmpty : ('key, 'value) dictionary -> bool *) let isEmpty dictionary = (dictionary = []) (* add : 'key -> 'value -> ('key, 'value) dictionary -> ('key, 'value) dictionary NB: The add operation requires unit time, i.e., the time required to form a pair plus the time to form a cons. *) let add key value dictionary = (key, value) :: dictionary (* find : 'key -> ('key, 'value) dictionary -> 'value option NB: The find opration requires linear time. I.e., the amount of work done grows linearly with the length of the dictionary. *) let rec find key dictionary = match dictionary with | [] -> None | (key', value) :: dictionary -> match key = key' with | true -> Some value | false -> find key dictionary (* Binary Search Trees If we can order the keys we can speed up the find operation by judicously order the key/value pairs in such a way that if we're doing a find, we can ignore half of the key/value pairs each time we do a compare. *) type ('key, 'value) dictionary = | Empty | Node of {key : 'key; value : 'value; left : ('key, 'value) dictionary; right : ('key, 'value) dictionary} (* isEmpty : ('key, 'value) dictionary -> bool *) let isEmpty dictionary = (dictionary = Empty) (* add : 'key -> 'value -> ('key, 'value) dictionary -> ('key, 'value) dictionary *) let rec add key' value' dictionary = match dictionary with | Empty -> Node {key = key'; value = value'; left = Empty; right = Empty} | Node {key; value; left; right} -> (match key' < key with | true -> let newLeft = add key' value' left in Node {key = key; value = value; left = newLeft; right = right} | false -> let newRight = add key' value' right in Node {key = key; value = value; left = left; right = newRight}) (* find : 'key -> ('key, 'value) dictionary -> 'value option *) let rec find key' dictionary = match dictionary with | Empty -> None | Node {key; value; left; right} -> (match key' = key with | true -> Some value | false -> (match key' < key with | true -> find key' left | false -> find key' right))