Tuples Another Example
Total Page:16
File Type:pdf, Size:1020Kb
Tuples CMSC 330: Organization of Constructed using (e1, ..., en) Programming Languages Deconstructed using pattern matching • Patterns involve parens and commas, e.g., (p1,p2, …) Tuples are like C structs • But without field labels OCaml 2 • Allocated on the heap Higher Order Functions Tuples can be heterogenous • Unlike lists, which must be homogenous • (1, ["string1"; "string2"]) is a valid tuple CMSC 330 - Spr ing 2016 1 CMSC 330 - Spr ing 2016 2 Examples With Tuples Another Example let plusThree (x, y, z) = x + y + z let addOne (x, y, z) = (x+1, y+1, z+1) let f l = match l with x::(_::y) -> (x,y) • plusThree (addOne (3, 4, 5)) (* returns 15 *) What is f [1;2;3;4]? Possibilities: ([1],[3]) let sum ((a, b), c) = (a+c, b+c) • sum ((1, 2), 3) = (4, 5) (1,3) (1,[3]) let plusFirstTwo (x::y::_, a) = (x + a, y + a) (1,4) • plusFirstTwo ([1; 2; 3], 4) = (5, 6) (1,[3;4]) let tls (_::xs, _::ys) = (xs, ys) • tls ([1; 2; 3], [4; 5; 6; 7]) = ([2; 3], [5; 6; 7]) Remember, semicolon for lists, comma for tuples • [1, 2] = [(1, 2)] = a list of size one • (1; 2) = Warning: This expression should have type unit CMSC 330 - Spr ing 2016 3 CMSC 330 - Spr ing 2016 4 1 List And Tuple Types List And Tuple Types Tuple types use * to separate components Tuple types use * to separate components Examples Examples • (1, 2) : • (1, 2) : int * int • (1, "string", 3.5) : • (1, "string", 3.5) : int * string * float • (1, ["a"; "b"], 'c') : • (1, ["a"; "b"], 'c') : int * string list * char • [(1,2)] : • [(1,2)] : (int * int) list • [(1, 2); (3, 4)] : • [(1, 2); (3, 4)] : (int * int) list • [(1,2); (1,2,3)] : • [(1,2); (1,2,3)] : error Ø Because the first list element has type int * int, but the second has type int * int * int – list elements must all be of the same type CMSC 330 - Spr ing 2016 5 CMSC 330 - Spr ing 2016 6 Polymorphic Types Examples Of Polymorphic Types Some functions we saw require specific list types let tl (_::t) = t • let plusFirstTwo (x::y::_, a) = (x + a, y + a) • tl : 'a list -> 'a list • plusFirstTwo : int list * int -> (int * int) let swap (x, y) = (y, x) But other functions work for any list • swap : 'a * 'b -> 'b * 'a • let hd (h::_) = h • hd [1; 2; 3] (* returns 1 *) let tls (_::xs, _::ys) = (xs, ys) • hd ["a"; "b"; "c"] (* returns "a" *) • tls : 'a list * 'b list -> 'a list * 'b list OCaml gives such functions polymorphic types • hd : 'a list -> 'a let eq (x,y) = x = y • this says the function takes a list of any element type • eq : 'a * ’a -> bool 'a, and returns something of that type CMSC 330 - Spr ing 2016 7 CMSC 330 - Spr ing 2016 8 2 Tuples Are A Fixed Size Conditionals This OCaml definition Use if...then...else like C/Java/Ruby • # let foo x = match x with • But no parentheses, no elsif, and no end (a, b) -> a + b if grade >= 90 then | (a, b, c) -> a + b + c;; print_string "You got an A" else if grade >= 80 then Would yield this error message print_string "You got a B" • This pattern matches values of type 'a * 'b * 'c else if grade >= 70 then but is here used to match values of type 'd * 'e print_string "You got a C" else print_string "You’re not doing so well" Tuples of different size have different types • Thus never more than one match case with tuples CMSC 330 - Spr ing 2016 9 CMSC 330 - Spr ing 2016 10 Conditionals (cont.) The Factorial Function In OCaml, conditionals return a result Using conditionals & functions • The value of whichever branch is true/false • Can you write fact, the factorial function? • Like ? : in C, C++, and Java let rec fact n = # if 7 > 42 then "hello" else “goodbye";; if n = 0 then - : string = "goodbye" 1 # let x = if true then 3 else 4;; else n * fact (n-1);; x : int = 3 # if false then 3 else 3.0;; This expression has type float but is Notice no return statements here used with type int • This is pretty much how it needs to be written CMSC 330 - Spr ing 2016 11 CMSC 330 - Spr ing 2016 12 3 Let Rec Let – More Examples The rec part means “define a recursive let f n = 10;; function” let f n = if n = 0 then 1 else n * f (n – 1);; Let vs. let rec • let x = e1 in e2 x in scope within e2 • f 0;; (* 1 *) • f 1;; • let rec x = e1 in e2 x in scope within e2 and e1 (* 10 *) Why use let rec? let f x = … f … in … f … If you used let instead of let rec to define fact • • (* Unbound value f *) let fact n = if n = 0 then 1 let rec f x = … f … in … f … else n * fact (n-1) in e2 • (* Bound value f *) Fact is not bound here! CMSC 330 - Spr ing 2016 13 CMSC 330 - Spr ing 2016 14 Recursion = Looping Lists and Recursion Recursion is essentially the only way to iterate Lists have a recursive structure • (The only way we’re going to talk about) • And so most functions over lists will be recursive let rec length l = match l with Another example [] -> 0 | (_::t) -> 1 + (length t) let rec print_up_to (n, m) = print_int n; print_string "\n"; if n < m then print_up_to (n + 1, m) • This is just like an inductive definition Ø The length of the empty list is zero Ø The length of a nonempty list is 1 plus the length of the tail • Type of length? CMSC 330 - Spr ing 2016 15 CMSC 330 - Spr ing 2016 16 4 More Examples More Examples (cont.) sum l (* sum of elts in l *) (* return a list containing all the elements in the let rec sum l = match l with list l followed by all the elements in list m *) [] -> 0 append (l, m) | (x::xs) -> x + (sum xs) let rec append (l, m) = match l with [] -> m negate l (* negate elements in list *) | (x::xs) -> x::(append (xs, m)) let rec negate l = match l with [] -> [] rev l (* reverse list; hint: use append *) | (x::xs) -> (-x) :: (negate xs) let rec rev l = match l with [] -> [] last l (* last element of l *) | (x::xs) -> append ((rev xs), [x]) let rec last l = match l with [x] -> x rev takes O(n2) time. Can you do better? | (x::xs) -> last xs CMSC 330 - Spr ing 2016 17 CMSC 330 - Spr ing 2016 18 A Clever Version of Reverse More Examples let rec rev_helper (l, a) = match l with flattenPairs l (* ('a * 'a) list -> 'a list *) [] -> a let rec flattenPairs l = match l with | (x::xs) -> rev_helper (xs, (x::a)) [] -> [] let rev l = rev_helper (l, []) | ((a, b)::t) -> a :: b :: (flattenPairs t) Let’s give it a try take (n, l) (* return first n elts of l *) let rec take (n, l) = rev [1; 2; 3] → if n = 0 then [] rev_helper ([1;2;3], []) → else match l with rev_helper ([2;3], [1]) → [] -> [] rev_helper ([3], [2;1]) → | (x::xs) -> x :: (take (n-1, xs)) rev_helper ([], [3;2;1]) → [3;2;1] CMSC 330 - Spr ing 2016 19 CMSC 330 - Spr ing 2016 20 5 Working With Lists Anonymous Functions Several of these examples have the same flavor Recall code blocks in Ruby • Walk through the list and do something to every (1..10).each { |x| print x } element • Here, we can think of { |x| print x } as a function • Walk through the list and keep track of something We can do this (and more) in Ocaml Recall the following example code from Ruby: range_each (1,10) (fun x -> print_int x) a = [1,2,3,4,5] b = a.collect { |x| -x } let rec range_each (i,j) f = • where if i > j then () • Here we passed a code block into the collect method else • Wouldn’t it be nice to do the same in OCaml? let _ = f i in (* ignore result *) range_each (i+1,j) f CMSC 330 - Spr ing 2016 21 CMSC 330 - Spr ing 2016 22 Anonymous Functions All Functions Are Anonymous Passing functions around is very common Functions are first-class, so you can bind them • So often we don’t want to bother to give them names to other names as you like let f x = x + 3 Use fun to make a function with no name let g = f Parameter Body g 5 = 8 In fact, let for functions is just shorthand fun x -> x + 3 let f x = body (fun x -> x + 3) 5 = 8 ↓ stands for let f = fun x -> body CMSC 330 - Spr ing 2016 23 CMSC 330 - Spr ing 2016 24 6 Examples Higher-Order Functions let next x = x + 1 In OCaml you can pass functions as arguments, • Short for let next = fun x -> x + 1 and return functions as results let plus (x, y) = x + y let plus_three x = x + 3 • Short for let plus = fun (x, y) -> x + y let twice f z = f (f z) twice plus_three 5 • Which is short for twice : ('a->'a) -> 'a -> 'a Ø let plus = fun z -> (match z with (x, y) -> x + y) let plus_four x = x + 4 let pick_fn n = let rec fact n = if n > 0 then plus_three else plus_four if n = 0 then 1 else n * fact (n-1) (pick_fn 5) 0 • Short for let rec fact = fun n -> pick_fn : int -> (int->int) (if n = 0 then 1 else n * fact (n-1)) CMSC 330 - Spr ing 2016 25 CMSC 330 - Spr ing 2016 26 Currying Curried Functions In OCaml We just saw a way for a function to take multiple OCaml has a really simple syntax for currying arguments let add x y = x + y • The function consumes one argument at a time, • This is identical to all of the following: returning a function that takes the rest let add = (fun x -> (fun y -> x + y)) let add = (fun x y -> x + y) let add x = (fun y -> x+y) This is called curryingthe function Thus: • Named after the logician Haskell B.