(* file: reviewOfSums.ml author: Bob Muller Lecture Notes for Meeting 2 of Week 2 of CS1103. Fall 2016 Notes: These notes are best viewed in the Atom editor. Announcements: Jesse Mu announced the BC Computer Science Society. Topics: 1. Review of Sum Types 2. Functions - definitions & uses - simplification of function calls 3. Background on Functions 4. A First Look at Repetition 5. Not covered in class: how to use Image.line and Image.polygon see the code in the lines/ folder. Usage: You can run the images code in these notes from the unix shell by typing: > make > ./go You can run all but the images in the OCaml REPL by commenting out lines 272 and 317 and removing the comments from 271 and 316. *) open World open Image open Color open Cs1103 let pi = acos(-1.) let displayWidth = 800.0 let displayHeight = displayWidth (* 1. Review of Sum Types **************************************) type fruit = Apple | Orange | Mango | Lemon (* NB: there are a number of important built-in sum types including the type of booleans: type bool = true | false The particular symbols true and false are the only symbolic constants that start with a lower case letter. *) (* isCitrus : fruit -> bool *) let isCitrus someFruit = match someFruit with | Apple | Mango -> false | Orange | Lemon -> true (* Terminology & Notation: A function with a return type of bool is often called a predicate. A predicate checking property Prop is often (but not always) named isProp. isFactor : int -> int -> bool *) let isFactor m n = (n mod m) = 0 (* Example: A function that randomly returns either a Lemon or a Mango. lemonOrMango : unit -> fruit We can use a random number generating function from the Standard Library Random. When the function: Random.int : int -> int is called as in (Random.int k), it will return a randomly chosen integer in the range {0, 1, ..., k-1}. lemonOrMango : unit -> fruit *) let lemonOrMango () = match Random.int 2 = 0 with | true -> Lemon | false -> Mango (* NB: In the special case where the expression being matched is of type bool (as above), we could opt to use an if-expression rather than a match-expression: *) let lemonOrMango () = if (Random.int 2) = 0 then Lemon else Mango (* Or, as a one-liner: *) let lemonOrMango () = if (Random.int 2) = 0 then Lemon else Mango (* In this course, we're usually going to stick with match expressions. A sum type for vegetables. *) type vegetable = Eggplant | Potato | Broccoli (* The type edible is a sum of sums. *) type edible = Fruit of fruit | Vegetable of vegetable (* The constructor Fruit builds a value of type edible when provided with (applied to) a value of type fruit and the constructor Vegetable builds a value of type edible when provided with a value of type vegetable. Someone likes Apples, Mangos, Eggplant & Broccoli. likes : edible -> bool *) let likes food = match food with | Fruit fruit -> not (isCitrus fruit) | Vegetable veggie -> (match veggie with | Eggplant | Broccoli -> true | Potato -> false) (* 2. Functions ************************************************ Definitions: let f x = expr1 Uses (or calls or applications): (f expr2) (or sometimes just: f expr2) NB: A function has exactly one definition but probably many uses. Terminology: + The f in line 142 is the name of the function, + the x in line 142 is a variable, the formal parameter, (think of this as an input portal) + the expr1 in line 142 is the body of the function, + the expr2 in line 146 is the actual argument (or argument). How to simplify a function call: 1. Simplify expr2 to its value V2 if it has one, 2. Replace all^* occurrences of x in expr1 with V2, 3. Simplify the resulting expression to its value V1 (if it has one), 4. The value of the function call is V1. Example: let double n = n * 2 double (double (2 + 3)) -> double (double 5) -> double (5 * 2) -> double 10 -> 10 * 2 -> 20 3. Background on Functions *********************************** The concept of a function has developed in fits and starts over several thousand years. In modern times, functions are very well understood. Two complementary ways to understand them are: 1. functions as graphs (i.e., sets of (input, output) pairs): double = {(0, 0), (1, 2), (2, 4), (3, 6), ... } 2. functions as rules: double(n) = n * 2 The former view (the graph) was developed in the 19th and early 20th centuries after the development of the theory of sets by George Cantor. The latter view was discovered by Alonzo Church in ~1930, it is THE key idea underlying modern coding. We'll have more to say about it later. 4. A First Look at Repetition ******************************** Consider the famous Fibonacci sequence: 1, 1, 2, 3, 5, 8, 13, 21, 34, ... A mathematician might write something like: +- 1 if n < 3 | F_n = + | +- F_(n-1) + F_(n-2) if n >= 3 In computing, we're going to need to define a function that uses repetition. fibonacci : int -> int *) let rec fibonacci n = match n < 3 with | true -> 1 | false -> fibonacci (n - 1) + fibonacci (n - 2) (* NB: The keyword rec indicates that the definition of the fibonacci function is recursive. This definition is recursive because we are using the function in its own definition. (!) Try the fibonacci function out on various inputs: 10, 20, 30, 40 and 45. You'll find that (fibonacci 40) and (fibonacci 45) take a while. We'll come back to this very (!) important topic next week. 5. How to use Image.line and Image.polygon ******************* The Image library's method of laying out sequences of points is a little bit counter-intuitive. The first issue, as we've said, is that y=0 is at the top rather than the bottom. This is fairly common for computer graphics systems. The other issue is that when lines or polygons are being computed using lists of points, each successive point in the list is specified relative to the previous one --- so in [(x1, y1); (x2, y2)] x2 specifies how much to change x1 to get to the second point, and likewise for y2. First a little helper function. outline : float -> float -> Image.t *) let outline width height = Image.rectangle width height ~fill:false ~outline_size:1.0 black (* Example 1: A simple diagonal line at a 45 degree angle. diagonal : float -> -> Color.t -> Image.t *) let diagonal length color = let angle = pi /. 4.0 in (* 45 degrees *) let opp = (sin angle) *. length in let adj = (cos angle) *. length in Image.line [(adj, -. opp)] color ~size:30. let drawDiagonal () = let color = Cs1103.randomColor() in let side = 400. in let length = sqrt (side ** 2.0 +. side ** 2.0) in let line = diagonal length color in let outlined = Image.place_image line (0., 0.) (outline side side) in let empty = Image.empty_scene displayWidth displayHeight in Image.place_image outlined (100., 100.) empty (* let go () = *) let () = World.big_bang () ~name:"A Diagonal" ~width: (f2I displayWidth) ~height:(f2I displayHeight) ~to_draw:drawDiagonal (* NB: That the line is bounded by a rectangle, shown in outline form in the example. The upper lefthand corner of the rectangle is defined by the minimum x and y coordinates of the points in the line. Example 2: A --- lets create an image for the letter A. theLetterA : float -> -> float -> Color.t -> Image.t A call (theLetterA width height color) produces an image of the letter A of the given width, height and color. The image can be produced as a composition of two lines, one with 2 segments and the other, the crossbar, with 1 segment. We'll put the crossbar halfway up the height of the letter. *) let theLetterA width height color = let (halfWidth, halfHeight) = (width /. 2.0, height /. 2.0) in let (x1, y1) = (halfWidth, -. height) in let (x2, y2) = (halfWidth, height) in let hypotenuse = (sqrt (height ** 2.0 +. halfWidth ** 2.0)) /. 2.0 in let x0 = sqrt (hypotenuse ** 2.0 -. halfHeight ** 2.0) in let barLength = width -. (2.0 *. x0) in let tent = Image.line [(x1, y1); (x2, y2)] color ~size:30. in let crossbar = Image.line [(barLength, 0.0)] color ~size:30.0 in let letter = Image.place_image crossbar (x0, halfHeight) tent in let outlined = Image.place_image letter (0., 0.) (outline width height) in let empty = Image.empty_scene displayWidth displayHeight in Image.place_image outlined (200., 200.) empty let drawA () = let color = Cs1103.randomColor() in theLetterA 300. 400. color (* let go () = *) let () = World.big_bang () ~name:"The letter A" ~width: (f2I displayWidth) ~height:(f2I displayHeight) ~to_draw:drawA