(* CS:3820, Fall 2018 *) (* Cesare Tinelli *) (* The University of Iowa *) (* F# examples seen in class *) type ilist = E | L of int * ilist let l = L(1, L(2, L(3, E))) // returns the first element of a non-empty ilist let first l = match l with | E -> failwith "list is empty!" | L(h, _) -> h // returns the rest a non-empty ilist let rest l = match l with | E -> failwith "list is empty!" | L(_, t) -> t // returns last element of a non-empty ilist l // based on the fact that the last element of l is: // 1) the first element of l, if l has only one element // 2) the last element of the rest of l, otherwise let rec last l = match l with | E -> failwith "list is empty!" | L(h, E) -> h | L(h, t) -> last t last (L(1, L(2, L(3, E)))) // returns the maximum element of a non-empty ilist let max l = let rec max2 n l = match l with | E -> n | L(h, t) -> if h > n then max2 h t else max2 n t in match l with | E -> failwith "list is empty!" | L(h, t) -> max2 h t max (L(1, L(2, L(3, E)))) max (L(1, L(3, L(-3, E)))) (* the ilist type can be made parametric (generic) wrt the the element type *) type 'a plist = E | L of 'a * 'a plist // 'a is a type variable let l1 = L(1, L(2, E)) let l2 = L("a", L("b", E)) // function last is now _polymorphic_ // it stands for an infinite family of last functions, // one for each possible element type let rec last l = match l with | E -> failwith "list is empty!" | L(h, E) -> h | L(h, t) -> last t last l1 last l2 // max too become _polymorphic_ but // only for types for which < is defined // (bounded polymorphism) let max l = let rec max2 n l = match l with | E -> n | L(h, t) -> if h > n then max2 h t else max2 n t in match l with | E -> failwith "list is empty!" | L(h, t) -> max2 h t let l3 = L("a", L("e", L("b", L("c", E)))) // what does this return? max l3 (* Polymorphic functions in general *) let id x = x id 5 id 3.3 let toTriple x y z = (x, y, z) // forces first two parameters to be of the same type let toTriple1 (x:'a) (y:'a) (z:'b) = (x, y, z) let equal x y = (x = y) let distinct x y = not (x = y) (* F# Lists *) [] [3; 5; 6; 3] ["dd"; "sdf"] [(1,2); (1,4)] [[3;1]; []; [4;5;6]] (* List elements must be of the same type! [3; "sdf"] *) [6] 6::[] // [] is the empty list constructor // (corresponding to E in plist) // :: is the non-empty list constructor // (corresponding to L in Plist) 6 :: [] // analogous to L(6, E) 5::(6::[]) 5::6::[] // :: is right-associative // [1; 2; 3; 4] is just a short form of 1 :: 2 :: 3 :: 4 :: [] (* The second argument of :: must be a list! 5 :: 6 *) // predefined list functions are in the List module List.head [1;2;3] List.tail [1;2;3] // The :: and [] constructors can be used in patterns let a :: [] = [7] let (f :: r) = [1;2;3] let (h :: t) = (1 :: (2 :: (3 :: []))) let rec len l = match l with | [] -> 0 | _ :: t -> 1 + len t len [2; 5; 1] len ["a"; "b"] len (1 :: 2 :: 3 :: []) (* --> 1 + len (2 :: 3 :: []) --> 1 + 1 + len (3 :: []) --> 1 + 1 + 1 + len [] --> 1 + 1 + 1 + 0 --> 3 *) let rec max l = match l with | [] -> failwith "Empty list!" | n :: [] -> n | n1 :: n2 :: t when n1 > n2 -> max (n1 :: t) | n1 :: n2 :: t -> max (n2 :: t) max [1;4;9;0;3] (* max (1 :: 4 :: 9 :: 0 :: 3 :: []) --> max (4 :: 9 :: 0 :: 3 :: []) --> max (9 :: 0 :: 3 :: []) --> max (9 :: 3 :: []) --> max (9 :: []) --> 9 *) let rec append l m = match l with [] -> m | h :: t -> h :: (append t m) append [1;2] [2;3;4] // append is actually predefined and has infix syntax [1;2] @ [2;3;4] let rec reverse = function [] -> [] | h :: t -> append (reverse t) [h] reverse [1;2;3] // association lists are lists of type ('a * 'b) list // they can be seen as map/dictionaries with keys of type 'a and values of type 'b let al = [("a", 3); ("c", 78); ("baf", 676); ("b", 110)] let rec lookup al x = match al with | [] -> failwith "key not found" | (k, v) :: t when x = k -> v | _ :: t -> lookup t x (* From Chap 1 of the Sestoft *) (* Language of expressions with variables *) type expr = | CstI of int | Var of string // new | Prim of string * expr * expr // 3 + 8 let e1 = Prim("+", CstI 3, CstI 8) // 3 + c let e2 = Prim("+", CstI 3, Var "c") // (b * 9) + a let e3 = Prim("+", Prim("*", Var "b", CstI 9), Var "a") (* Evaluation within an environment *) type env = (string * int) list let rec eval e (env : env) : int = match e with | CstI i -> i | Var x -> lookup env x | Prim("+", e1, e2) -> eval e1 env + eval e2 env | Prim("*", e1, e2) -> eval e1 env * eval e2 env | Prim("-", e1, e2) -> eval e1 env - eval e2 env | Prim _ -> failwith "unknown primitive" let env = [] let v1 = eval e1 env eval e2 env let env1 = [("c", 4)] let v2 = eval e2 env1 let v2a = eval e2 (("c", 100) :: env1) // new value for c hides old one in env1 let v3 = eval e3 [("a", 10); ("b", 3)]