Booleans (aka Truth Values) Programming Languages and Compilers (CS 421) Booleans and Short-Circuit Evaluation. Tuples as Values.

Similar documents
# true;; - : bool = true. # false;; - : bool = false 9/10/ // = {s (5, "hi", 3.2), c 4, a 1, b 5} 9/10/2017 4

Programming Languages and Compilers (CS 421)

Programming Languages and Compilers (CS 421)

Forward Recursion. Programming Languages and Compilers (CS 421) Mapping Recursion. Forward Recursion: Examples. Folding Recursion.

Functions Over Lists. Programming Languages and Compilers (CS 421) Structural Recursion. Functions Over Lists. Structural Recursion : List Example

Programming Languages and Compilers (CS 421)

CS 321 Programming Languages

Programming Languages and Compilers (CS 421)

n What is its running time? 9/18/17 2 n poor_rev [1,2,3] = n (poor_rev [1] = n ((poor_rev [1] =

Forward recursion. CS 321 Programming Languages. Functions calls and the stack. Functions calls and the stack

CS Lecture 6: Map and Fold. Prof. Clarkson Spring Today s music: Selections from the soundtrack to 2001: A Space Odyssey

CS Lecture 6: Map and Fold. Prof. Clarkson Fall Today s music: Selections from the soundtrack to 2001: A Space Odyssey

FUNCTIONAL PROGRAMMING

Map and Fold. Prof. Clarkson Fall Today s music: Selections from the soundtrack to 2001: A Space Odyssey

CSCI-GA Scripting Languages

Tuples. CMSC 330: Organization of Programming Languages. Examples With Tuples. Another Example

Programming Languages and Compilers (CS 421)

OCaml. ML Flow. Complex types: Lists. Complex types: Lists. The PL for the discerning hacker. All elements must have same type.

List Functions, and Higher-Order Functions

Introduction to Scheme

Recap: Functions as first-class values

Programming Languages and Compilers (CS 421)

CMSC 330: Organization of Programming Languages. Functional Programming with Lists

CSE 341 Section 5. Winter 2018

CMSC 330, Fall 2013, Practice Problems 3

Principles of Programming Languages Topic: Functional Programming Professor L. Thorne McCarty Spring 2003

Programming Languages and Compilers (CS 421)

n (0 1)*1 n a*b(a*) n ((01) (10))* n You tell me n Regular expressions (equivalently, regular 10/20/ /20/16 4

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

Tail Calls. CMSC 330: Organization of Programming Languages. Tail Recursion. Tail Recursion (cont d) Names and Binding. Tail Recursion (cont d)

n Takes abstract syntax trees as input n In simple cases could be just strings n One procedure for each syntactic category

CSE341: Programming Languages Lecture 6 Nested Patterns Exceptions Tail Recursion. Zach Tatlock Winter 2018

Introduction to OCaml

News. CSE 130: Programming Languages. Environments & Closures. Functions are first-class values. Recap: Functions as first-class values

CS 11 Ocaml track: lecture 2

COP4020 Programming Languages. Functional Programming Prof. Robert van Engelen

FOFL and FOBS: First-Order Functions

CS 457/557: Functional Languages

Processadors de Llenguatge II. Functional Paradigm. Pratt A.7 Robert Harper s SML tutorial (Sec II)

Introduction to Objective Caml

Overview. A normal-order language. Strictness. Recursion. Infinite data structures. Direct denotational semantics. Transition semantics

Why OCaml? Introduction to Objective Caml. Features of OCaml. Applications written in OCaml. Stefan Wehr

CSE341: Programming Languages Lecture 9 Function-Closure Idioms. Dan Grossman Winter 2013

LECTURE 16. Functional Programming

Lecture 4: Higher Order Functions

Programming Languages. Tail Recursion. CSE 130: Winter Lecture 8: NOT TR. last thing function does is a recursive call

Programming Languages & Compilers. Programming Languages and Compilers (CS 421) Programming Languages & Compilers. Major Phases of a Compiler

COSE212: Programming Languages. Lecture 3 Functional Programming in OCaml

Programming Languages and Compilers (CS 421)

Programming Languages & Compilers. Programming Languages and Compilers (CS 421) I. Major Phases of a Compiler. Programming Languages & Compilers

Programming Languages and Compilers (CS 421)

Introduction to ML. Mooly Sagiv. Cornell CS 3110 Data Structures and Functional Programming

CS 11 Haskell track: lecture 1

Lecture 15 CIS 341: COMPILERS

SCHEME 8. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. March 23, 2017

CMSC 330: Organization of Programming Languages. Functional Programming with Lists

Datatype declarations

CS422 - Programming Language Design

CSE341: Programming Languages Lecture 9 Function-Closure Idioms. Dan Grossman Fall 2011

News. Programming Languages. Complex types: Lists. Recap: ML s Holy Trinity. CSE 130: Spring 2012

Begin at the beginning

Coq Summer School, Session 2 : Basic programming with numbers and lists. Pierre Letouzey

CMSC 330, Fall 2013, Practice Problem 3 Solutions

A Third Look At ML. Chapter Nine Modern Programming Languages, 2nd ed. 1

Functional Programming. Pure Functional Programming

Encodings. Using the minimal λ-calculus language we get. functions. local binding. booleans. numbers... and recursive functions?

Functional Programming. Pure Functional Programming

Introduction to ML. Mooly Sagiv. Cornell CS 3110 Data Structures and Functional Programming

SNU Programming Language Theory

Thinking Induc,vely. COS 326 David Walker Princeton University

Tail Recursion: Factorial. Begin at the beginning. How does it execute? Tail recursion. Tail recursive factorial. Tail recursive factorial

Advanced Type System Features Tom Schrijvers. Leuven Haskell User Group

The type checker will complain that the two branches have different types, one is string and the other is int

MoreIntro_annotated.v. MoreIntro_annotated.v. Printed by Zach Tatlock. Oct 04, 16 21:55 Page 1/10

Two Problems. Programming Languages and Compilers (CS 421) Type Inference - Example. Type Inference - Outline. Type Inference - Example

To figure this out we need a more precise understanding of how ML works

Programming Languages

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015

News. Programming Languages. Recap. Recap: Environments. Functions. of functions: Closures. CSE 130 : Fall Lecture 5: Functions and Datatypes

Introduction to ML. Based on materials by Vitaly Shmatikov. General-purpose, non-c-like, non-oo language. Related languages: Haskell, Ocaml, F#,

Tail Recursion. ;; a recursive program for factorial (define fact (lambda (m) ;; m is non-negative (if (= m 0) 1 (* m (fact (- m 1))))))

max function Next concat function max function What s the pattern? concat function More on recursion Higher-order functions

n n Try tutorial on front page to get started! n spring13/ n Stack Overflow!

Racket. CSE341: Programming Languages Lecture 14 Introduction to Racket. Getting started. Racket vs. Scheme. Example.

CS 360: Programming Languages Lecture 10: Introduction to Haskell

CPL 2016, week 10. Clojure functional core. Oleg Batrashev. April 11, Institute of Computer Science, Tartu, Estonia

Chapter 1. Fundamentals of Higher Order Programming

INTRODUCTION TO HASKELL

Haske k ll An introduction to Functional functional programming using Haskell Purely Lazy Example: QuickSort in Java Example: QuickSort in Haskell

Programming Languages

Mini-ML. CS 502 Lecture 2 8/28/08

Today s Plan. Programming Languages. Example : Factorial. Recursion. CSE 130 : Spring Lecture 6: Higher-Order Functions

Sometimes it s good to be lazy

Programming Languages

CSE341: Programming Languages Lecture 7 First-Class Functions. Dan Grossman Winter 2013

Side note: Tail Recursion. Begin at the beginning. Side note: Tail Recursion. Base Types. Base Type: int. Base Type: int

An introduction introduction to functional functional programming programming using usin Haskell

Introduction to ML. Mooly Sagiv. Cornell CS 3110 Data Structures and Functional Programming

School of Computer Science

CMSC 330: Organization of Programming Languages. Formal Semantics of a Prog. Lang. Specifying Syntax, Semantics

Transcription:

Booleans (aka Truth Values) Programming Languages and Compilers (CS 421) Elsa L Gunter 2112 SC, UIUC https://courses.engr.illinois.edu/cs421/fa2017/cs421d # true;; - : bool = true # false;; - : bool = false // ρ 7 = {c 4, test 3.7, a 1, b 5} # if b > a then 25 else 0;; - : int = 25 Based in part on slides by Mattox Beckman, as updated by Vikram Adve and Gul Agha 9/5/17 1 9/5/17 2 Booleans and Short-Circuit Evaluation # 3 > 1 && 4 > 6;; - : bool = false # 3 > 1 4 > 6;; - : bool = true # (print_string "Hi\n"; 3 > 1) 4 > 6;; Hi - : bool = true # 3 > 1 (print_string "Bye\n"; 4 > 6);; - : bool = true # not (4 > 6);; - : bool = true 9/5/17 3 Tuples as Values // ρ 7 = {c 4, test 3.7, a 1, b 5} # let s = (5,"hi",3.2);; val s : int * string * float = (5, "hi", 3.2) // ρ 8 = {s (5, "hi", 3.2), c 4, test 3.7, a 1, b 5} a è 1 a è 1 b è 5 test è 3.7 c è 4 b è 5 test è 3.7 c è 4 s è (5, hi, 3.2) 9/5/17 4 Pattern Matching with Tuples / ρ 8 = {s (5, "hi", 3.2), c 4, test 3.7, a 1, b 5} # let (a,b,c) = s;; (* (a,b,c) is a pattern *) val a : int = 5 val b : string = "hi" a è 5 b è hi val c : float = 3.2 # let x = 2, 9.3;; (* tuples don't require parens in Ocaml *) b è hi val x : int * float = (2, 9.3) a è 1 b è 5 test è 3.7 c è 4 s è (5, hi, 3.2) test è 3.7 s è (5, hi, 3.2) c è 3.2 a è 5 test è 3.7 s è (5, hi, 3.2) c è 3.2 x è (2, 9.3) 9/5/17 5 Nested Tuples # (*Tuples can be nested *) let d = ((1,4,62),("bye",15),73.95);; val d : (int * int * int) * (string * int) * float = ((1, 4, 62), ("bye", 15), 73.95) # (*Patterns can be nested *) let (p,(st,_),_) = d;; (* _ matches all, binds nothing *) val p : int * int * int = (1, 4, 62) val st : string = "bye" 9/5/17 6

Functions on tuples # let plus_pair (n,m) = n + m;; val plus_pair : int * int -> int = <fun> # plus_pair (3,4);; - : int = 7 # let double x = (x,x);; val double : 'a -> 'a * 'a = <fun> # double 3;; - : int * int = (3, 3) # double "hi";; - : string * string = ("hi", "hi") 9/5/17 7 Functions on tuples # let plus_pair (n,m) = n + m;; val plus_pair : int * int -> int = <fun> # plus_pair (3,4);; - : int = 7 # let double x = (x,x);; val double : 'a -> 'a * 'a = <fun> # double 3;; - : int * int = (3, 3) # double "hi";; - : string * string = ("hi", "hi") 9/5/17 8 Save the Environment! Closure for plus_pair n A closure is a pair of an environment and an association of a sequence of variables (the input variables) with an expression (the function body), written: < (v1,,vn) exp, ρ > n Where ρ is the environment in effect when the function is defined (for a simple function) n Assume ρ plus_pair was the environment just before plus_pair defined n Closure for fun (n,m) -> n + m: <(n,m) n + m, ρ plus_pair > n Environment just after plus_pair defined: {plus_pair <(n,m) n + m, ρ plus_pair >} + ρ plus_pair 9/5/17 9 9/5/17 10 Functions with more than one argument # let add_three x y z = x + y + z;; val add_three : int -> int -> int -> int = <fun> # let t = add_three 6 3 2;; val t : int = 11 # let add_three = fun x -> (fun y -> (fun z -> x + y + z));; val add_three : int -> int -> int -> int = <fun> Again, first syntactic sugar for second Curried vs Uncurried n Recall val add_three : int -> int -> int -> int = <fun> n How does it differ from # let add_triple (u,v,w) = u + v + w;; val add_triple : int * int * int -> int = <fun> n add_three is curried; n add_triple is uncurried 9/5/17 11 9/5/17 12

Curried vs Uncurried Partial application of functions # add_triple (6,3,2);; - : int = 11 # add_triple 5 4;; Characters 0-10: add_triple 5 4;; ^^^^^^^^^^ This function is applied to too many arguments, maybe you forgot a `;' # fun x -> add_triple (5,4,x);; : int -> int = <fun> 9/5/17 13 let add_three x y z = x + y + z;; # let h = add_three 5 4;; val h : int -> int = <fun> # h 3;; - : int = 12 # h 7;; - : int = 16 - Partial application also called sectioning 9/5/17 14 Recall: let plus_x = fun x => y + x Closure for plus_x let x = 12 X è 12 let plus_x = fun y => y + x let x = 7 plus_x è y y + x x è7 X è 12 plus_x è y y + x X è 12 9/5/17 15 X è 12 n When plus_x was defined, had environment: ρ plus_x = {, x 12, } n Recall: let plus_x y = y + x is really let plus_x = fun y -> y + x n Closure for fun y -> y + x: <y y + x, ρ plus_x > n Environment just after plus_x defined: {plus_x <y y + x, ρ plus_x >} + ρ plus_x 9/5/17 16 Evaluating declarations n Evaluation uses an environment ρ n To evaluate a (simple) declaration let x = e n Evaluate expression e in ρ to value v n Update ρ with x v: {x v} + ρ n Update: ρ 1 + ρ 2 has all the bindings in ρ 1 and all those in ρ 2 that are not rebound in ρ 1 {x 2, y 3, a hi } + {y 100, b 6} = {x 2, y 3, a hi, b 6} Evaluating expressions n Evaluation uses an environment ρ n A constant evaluates to itself n To evaluate an variable, look it up in ρ (ρ(v)) n To evaluate uses of +, _, etc, eval args, then do operation n Function expression evaluates to its closure n To evaluate a local dec: let x = e1 in e2 n Eval e1 to v, eval e2 using {x v} + ρ 9/5/17 17 9/5/17 18

Evaluation of Application with Closures n Given application expression f(e 1,,e n ) n In environment ρ, evaluate left term to closure, c = <(x 1,,x n ) b, ρ> n (x 1,,x n ) variables in (first) argument n Evaluate (e 1,,e n ) to value (v 1,,v n ) n Update the environment ρ to ρ = {x 1 v 1,, x n v n }+ ρ n Evaluate body b in environment ρ 9/5/17 19 Evaluation of Application of plus_x;; n Have environment: ρ = {plus_x <y y + x, ρ plus_x >,, y 3, } where ρ plus_x = {x 12,, y 24, } n Eval (plus_x y, ρ) rewrites to n App (Eval(plus_x, ρ), Eval(y, ρ)) rewrites to n App (<y y + x, ρ plus_x >, 3) rewrites to n Eval (y + x, {y 3} +ρ plus_x ) rewrites to n Eval (3 + 12, ρ plus_x ) = 15 9/5/17 20 Evaluation of Application of plus_pair n Assume environment ρ = {x 3, plus_pair <(n,m) n + m, ρ plus_pair >} + ρ plus_pair n Eval (plus_pair (4,x), ρ)= n App (Eval (plus_pair, ρ), Eval ((4,x), ρ)) = n App (<(n,m) n + m, ρ plus_pair >, (4,3)) = n Eval (n + m, {n -> 4, m -> 3} + ρ plus_pair ) = n Eval (4 + 3, {n -> 4, m -> 3} + ρ plus_pair ) = 7 Closure question n If we start in an empty environment, and we execute: let f = fun n -> n + 5;; (* 0 *) let pair_map g (n,m) = (g n, g m);; let f = pair_map f;; let a = f (4,6);; What is the environment at (* 0 *)? 9/5/17 21 9/5/17 22 Answer let f = fun n -> n + 5;; ρ 0 = {f <n n + 5, { }>} Closure question n If we start in an empty environment, and we execute: let f = fun => n + 5;; let pair_map g (n,m) = (g n, g m);; (* 1 *) let f = pair_map f;; let a = f (4,6);; What is the environment at (* 1 *)? 9/5/17 23 9/5/17 24

Answer ρ 0 = {f <n n + 5, { }>} let pair_map g (n,m) = (g n, g m);; ρ 1 = {pair_map <g fun (n,m) -> (g n, g m), {f <n n + 5, { }>}>, f <n n + 5, { }>} Closure question n If we start in an empty environment, and we execute: let f = fun => n + 5;; let pair_map g (n,m) = (g n, g m);; let f = pair_map f;; (* 2 *) let a = f (4,6);; What is the environment at (* 2 *)? 9/5/17 25 9/5/17 26 Evaluate pair_map f ρ 0 = {f <n n + 5, { }>} ρ 1 = {pair_map <g fun (n,m) -> (g n, g m), ρ 0 >, f <n n + 5, { }>} let f = pair_map f;; Evaluate pair_map f ρ 0 = {f <n n + 5, { }>} ρ 1 = {pair_map <g fun (n,m) -> (g n, g m), ρ 0 >, f <n n + 5, { }>} Eval(pair_map f, ρ 1 ) = 9/5/17 27 9/5/17 28 Evaluate pair_map f ρ 0 = {f <n n + 5, { }>} ρ 1 = {pair_map <g fun (n,m) -> (g n, g m), ρ 0 >, f <n n + 5, { }>} Eval(pair_map f, ρ 1 ) = App (<g fun (n,m) -> (g n, g m), ρ 0 >, <n n + 5, { }>) = Evaluate pair_map f ρ 0 = {f <n n + 5, { }>} ρ 1 = {pair_map <g fun (n,m) -> (g n, g m), ρ 0 >, f <n n + 5, { }>} Eval(pair_map f, ρ 1 ) = App (<g fun (n,m) -> (g n, g m), ρ 0 >, <n n + 5, { }>) = Eval(fun (n,m)->(g n, g m), {g <n n + 5, { }>}+ρ 0 ) =<(n,m) (g n, g m), {g <n n + 5, { }>}+ρ 0 > =<(n,m) (g n, g m), {g <n n + 5, { }> f <n n + 5, { }>} 9/5/17 29 9/5/17 30

Answer ρ 1 = {pair_map <g fun (n,m) -> (g n, g m),{f <n n + 5, { }>}>, f <n n + 5, { }>} let f = pair_map f;; ρ 2 = {f <(n,m) (g n, g m), {g <n n + 5, { }>, f <n n + 5, { }>}>, pair_map <g fun (n,m) -> (g n, g m), {f <n n + 5, { }>}>} Closure question n If we start in an empty environment, and we execute: let f = fun => n + 5;; let pair_map g (n,m) = (g n, g m);; let f = pair_map f;; let a = f (4,6);; (* 3 *) What is the environment at (* 3 *)? 9/5/17 31 9/5/17 32 Final Evalution? Evaluate f (4,6);; ρ 2 = {f <(n,m) (g n, g m), {g <n n + 5, { }>, f <n n + 5, { }>}>, pair_map <g fun (n,m) -> (g n, g m), {f <n n + 5, { }>}>} let a = f (4,6);; ρ 2 = {f <(n,m) (g n, g m), {g <n n + 5, { }>, f <n n + 5, { }>}>, pair_map <g fun (n,m) -> (g n, g m), {f <n n + 5, { }>}>} Eval(f (4,6), ρ 2 ) = 9/5/17 33 9/5/17 34 Evaluate f (4,6);; Evaluate f (4,6);; ρ 2 = {f <(n,m) (g n, g m), {g <n n + 5, { }>, f <n n + 5, { }>}>, pair_map <g fun (n,m) -> (g n, g m), {f <n n + 5, { }>}>} Eval(f (4,6), ρ 2 ) = App(<(n,m) (g n, g m), {g <n n + 5, { }>, f <n n + 5, { }>}>, (4,6)) = App(<(n,m) (g n, g m), {g <n n + 5, { }>, f <n n + 5, { }>}>, (4,6)) = Eval((g n, g m), {n 4, m 6} + {g <n n + 5, { }>, f <n n + 5, { }>}) = (App(<n n + 5, { }>, 4), App (<n n + 5, { }>, 6)) = 9/5/17 35 9/5/17 36

Evaluate f (4,6);; (App(<n n + 5, { }>, 4), App (<n n + 5, { }>, 6)) = (Eval(n + 5, {n 4} + { }), Eval(n + 5, {n 6} + { })) = (Eval(4 + 5, {n 4} + { }), Eval(6 + 5, {n 6} + { })) = (9, 11) Functions as arguments # let thrice f x = f (f (f x));; val thrice : ('a -> 'a) -> 'a -> 'a = <fun> # let g = thrice plus_two;; val g : int -> int = <fun> # g 4;; - : int = 10 # thrice (fun s -> "Hi! " ^ s) "Good-bye!";; - : string = "Hi! Hi! Hi! Good-bye!" 9/5/17 37 9/5/17 38 Higher Order Functions n A function is higher-order if it takes a function as an argument or returns one as a result n Example: # let compose f g = fun x -> f (g x);; val compose : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun> n The type ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b is a higher order type because of ('a -> 'b) and ('c -> 'a) and -> 'c -> 'b Thrice n Recall: # let thrice f x = f (f (f x));; val thrice : ('a -> 'a) -> 'a -> 'a = <fun> n How do you write thrice with compose? 9/5/17 39 9/5/17 40 Thrice n Recall: # let thrice f x = f (f (f x));; val thrice : ('a -> 'a) -> 'a -> 'a = <fun> n How do you write thrice with compose? # let thrice f = compose f (compose f f);; val thrice : ('a -> 'a) -> 'a -> 'a = <fun> n Is this the only way? Lambda Lifting n You must remember the rules for evaluation when you use partial application # let add_two = (+) (print_string "test\n"; 2);; test val add_two : int -> int = <fun> # let add2 = (* lambda lifted *) fun x -> (+) (print_string "test\n"; 2) x;; val add2 : int -> int = <fun> 9/5/17 41 9/5/17 42

Lambda Lifting # thrice add_two 5;; - : int = 11 # thrice add2 5;; test test test - : int = 11 n Lambda lifting delayed the evaluation of the argument to (+) until the second argument was supplied Partial Application and Unknown Types n Recall compose plus_two: # let f1 = compose plus_two;; val f1 : ('_a -> int) -> '_a -> int = <fun> n Compare to lambda lifted version: # let f2 = fun g -> compose plus_two g;; val f2 : ('a -> int) -> 'a -> int = <fun> n What is the difference? 9/5/17 43 9/5/17 44 Partial Application and Unknown Types n _a can only be instantiated once for an expression # f1 plus_two;; - : int -> int = <fun> # f1 List.length;; Characters 3-14: f1 List.length;; ^^^^^^^^^^^ This expression has type 'a list -> int but is here used with type int -> int Partial Application and Unknown Types n a can be repeatedly instantiated # f2 plus_two;; - : int -> int = <fun> # f2 List.length;; - : '_a list -> int = <fun> 9/5/17 45 9/5/17 46 Match Expressions # let triple_to_pair triple = match triple with (0, x, y) -> (x, y) (x, 0, y) -> (x, y) (x, y, _) -> (x, y);; Each clause: pattern on left, expression on right Each x, y has scope of only its clause Use first matching clause val triple_to_pair : int * int * int -> int * int = <fun> Recursive Functions # let rec factorial n = if n = 0 then 1 else n * factorial (n - 1);; val factorial : int -> int = <fun> # factorial 5;; - : int = 120 # (* rec is needed for recursive function declarations *) 9/5/17 47 9/5/17 48

Recursion Example Recursion and Induction Compute n 2 recursively using: n 2 = (2 * n - 1) + (n - 1) 2 # let rec nthsq n = (* rec for recursion *) match n (* pattern matching for cases *) with 0 -> 0 (* base case *) n -> (2 * n -1) (* recursive case *) + nthsq (n -1);; (* recursive call *) val nthsq : int -> int = <fun> # nthsq 3;; - : int = 9 # let rec nthsq n = match n with 0 -> 0 n -> (2 * n - 1) + nthsq (n - 1) ;; n Base case is the last case; it stops the computation n Recursive call must be to arguments that are somehow smaller - must progress to base case n if or match must contain base case n Failure of these may cause failure of termination Structure of recursion similar to inductive proof 9/5/17 49 9/5/17 50 Lists Lists n First example of a recursive datatype (aka algebraic datatype) n Unlike tuples, lists are homogeneous in type (all elements same type) n List can take one of two forms: n Empty list, written [ ] n Non-empty list, written x :: xs n x is head element, xs is tail list, :: called cons n Syntactic sugar: [x] == x :: [ ] n [ x1; x2; ; xn] == x1 :: x2 :: :: xn :: [ ] 9/5/17 51 9/5/17 52 Lists # let fib5 = [8;5;3;2;1;1];; val fib5 : int list = [8; 5; 3; 2; 1; 1] # let fib6 = 13 :: fib5;; val fib6 : int list = [13; 8; 5; 3; 2; 1; 1] # (8::5::3::2::1::1::[ ]) = fib5;; - : bool = true # fib5 @ fib6;; - : int list = [8; 5; 3; 2; 1; 1; 13; 8; 5; 3; 2; 1; 1] Lists are Homogeneous # let bad_list = [1; 3.2; 7];; Characters 19-22: let bad_list = [1; 3.2; 7];; ^^^ This expression has type float but is here used with type int 9/5/17 53 9/5/17 54

Question Answer n Which one of these lists is invalid? n Which one of these lists is invalid? 1. [2; 3; 4; 6] 2. [2,3; 4,5; 6,7] 3. [(2.3,4); (3.2,5); (6,7.2)] 4. [[ hi ; there ]; [ wahcha ]; [ ]; [ doin ]] 1. [2; 3; 4; 6] 2. [2,3; 4,5; 6,7] 3. [(2.3,4); (3.2,5); (6,7.2)] 4. [[ hi ; there ]; [ wahcha ]; [ ]; [ doin ]] 3 is invalid because of last pair 9/5/17 55 9/5/17 56 Functions Over Lists # let rec double_up list = match list with [ ] -> [ ] (* pattern before ->, expression after *) (x :: xs) -> (x :: x :: double_up xs);; val double_up : 'a list -> 'a list = <fun> # let fib5_2 = double_up fib5;; val fib5_2 : int list = [8; 8; 5; 5; 3; 3; 2; 2; 1; 1; 1; 1] Functions Over Lists # let silly = double_up ["hi"; "there"];; val silly : string list = ["hi"; "hi"; "there"; "there"] # let rec poor_rev list = match list with [] -> [] (x::xs) -> poor_rev xs @ [x];; val poor_rev : 'a list -> 'a list = <fun> # poor_rev silly;; - : string list = ["there"; "there"; "hi"; "hi"] 9/5/17 57 9/5/17 58 Question: Length of list n Problem: write code for the length of the list n How to start? let length l = Question: Length of list n Problem: write code for the length of the list n How to start? let rec length l = match l with 9/5/17 59 9/5/17 60

Question: Length of list n Problem: write code for the length of the list n What patterns should we match against? let rec length l = match l with Question: Length of list n Problem: write code for the length of the list n What patterns should we match against? let rec length l = match l with [] -> (a :: bs) -> 9/5/17 61 9/5/17 62 Question: Length of list n Problem: write code for the length of the list n What result do we give when l is empty? let rec length l = match l with [] -> 0 (a :: bs) -> Question: Length of list n Problem: write code for the length of the list n What result do we give when l is not empty? let rec length l = match l with [] -> 0 (a :: bs) -> 9/5/17 63 9/5/17 64 Question: Length of list n Problem: write code for the length of the list n What result do we give when l is not empty? let rec length l = match l with [] -> 0 (a :: bs) -> 1 + length bs Same Length n How can we efficiently answer if two lists have the same length? 9/5/17 65 9/5/17 66

Same Length n How can we efficiently answer if two lists have the same length? let rec same_length list1 list2 = match list1 with [] -> (match list2 with [] -> true (y::ys) -> false) (x::xs) -> (match list2 with [] -> false (y::ys) -> same_length xs ys) Functions Over Lists # let rec map f list = match list with [] -> [] (h::t) -> (f h) :: (map f t);; val map : ('a -> 'b) -> 'a list -> 'b list = <fun> # map plus_two fib5;; - : int list = [10; 7; 5; 4; 3; 3] # map (fun x -> x - 1) fib6;; : int list = [12; 7; 4; 2; 1; 0; 0] 9/5/17 67 9/5/17 68 Iterating over lists # let rec fold_left f a list = match list with [] -> a (x :: xs) -> fold_left f (f a x) xs;; val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a = <fun> # fold_left (fun () -> print_string) () ["hi"; "there"];; hithere- : unit = () Iterating over lists # let rec fold_right f list b = match list with [] -> b (x :: xs) -> f x (fold_right f xs b);; val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b = <fun> # fold_right (fun s -> fun () -> print_string s) ["hi"; "there"] ();; therehi- : unit = () 9/5/17 69 9/5/17 70 Structural Recursion n Functions on recursive datatypes (eg lists) tend to be recursive n Recursion over recursive datatypes generally by structural recursion n Recursive calls made to components of structure of the same recursive type n Base cases of recursive types stop the recursion of the function Structural Recursion : List Example # let rec length list = match list with [ ] -> 0 (* Nil case *) x :: xs -> 1 + length xs;; (* Cons case *) val length : 'a list -> int = <fun> # length [5; 4; 3; 2];; - : int = 4 n Nil case [ ] is base case n Cons case recurses on component list xs 9/5/17 71 9/5/17 72

Forward Recursion n In Structural Recursion, split input into components and (eventually) recurse n Forward Recursion form of Structural Recursion n In forward recursion, first call the function recursively on all recursive components, and then build final result from partial results n Wait until whole structure has been traversed to start building answer Forward Recursion: Examples # let rec double_up list = match list with [ ] -> [ ] (x :: xs) -> (x :: x :: double_up xs);; val double_up : 'a list -> 'a list = <fun> # let rec poor_rev list = match list with [] -> [] (x::xs) -> poor_rev xs @ [x];; val poor_rev : 'a list -> 'a list = <fun> 9/5/17 73 9/5/17 74 Encoding Recursion with Fold # let rec append list1 list2 = match list1 with [ ] -> list2 x::xs -> x :: append xs list2;; val append : 'a list -> 'a list -> 'a list = <fun> Base Case Operation Recursive Call # let append list1 list2 = fold_right (fun x y -> x :: y) list1 list2;; val append : 'a list -> 'a list -> 'a list = <fun> # append [1;2;3] [4;5;6];; - : int list = [1; 2; 3; 4; 5; 6] Mapping Recursion n One common form of structural recursion applies a function to each element in the structure # let rec doublelist list = match list with [ ] -> [ ] x::xs -> 2 * x :: doublelist xs;; val doublelist : int list -> int list = <fun> # doublelist [2;3;4];; - : int list = [4; 6; 8] 9/5/17 75 9/5/17 76 Mapping Recursion n Can use the higher-order recursive map function instead of direct recursion # let doublelist list = List.map (fun x -> 2 * x) list;; val doublelist : int list -> int list = <fun> # doublelist [2;3;4];; - : int list = [4; 6; 8] n Same function, but no rec Folding Recursion n Another common form folds an operation over the elements of the structure # let rec multlist list = match list with [ ] -> 1 x::xs -> x * multlist xs;; val multlist : int list -> int = <fun> # multlist [2;4;6];; - : int = 48 n Computes (2 * (4 * (6 * 1))) 9/5/17 77 9/5/17 78

Folding Recursion n multlist folds to the right n Same as: # let multlist list = List.fold_right (fun x -> fun p -> x * p) list 1;; val multlist : int list -> int = <fun> # multlist [2;4;6];; - : int = 48 How long will it take? n Remember the big-o notation from CS 225 and CS 273 n Question: given input of size n, how long to generate output? n Express output time in terms of input size, omit constants and take biggest power 9/5/17 79 9/5/17 80 How long will it take? Common big-o times: n Constant time O (1) n input size doesn t matter n Linear time O (n) n double input double time n Quadratic time O (n 2 ) n double input quadruple time n Exponential time O (2 n ) n increment input double time Linear Time n Expect most list operations to take linear time O (n) n Each step of the recursion can be done in constant time n Each step makes only one recursive call n List example: multlist, append n Integer example: factorial 9/5/17 81 9/5/17 82 Quadratic Time n Each step of the recursion takes time proportional to input n Each step of the recursion makes only one recursive call. n List example: # let rec poor_rev list = match list with [] -> [] (x::xs) -> poor_rev xs @ [x];; val poor_rev : 'a list -> 'a list = <fun> Exponential running time n Hideous running times on input of any size n Each step of recursion takes constant time n Each recursion makes two recursive calls n Easy to write naïve code that is exponential for functions that can be linear 9/5/17 83 9/5/17 84

Exponential running time An Important Optimization # let rec naivefib n = match n with 0 -> 0 1 -> 1 _ -> naivefib (n-1) + naivefib (n-2);; val naivefib : int -> int = <fun> Normal call h g f n When a function call is made, the return address needs to be saved to the stack so we know to where to return when the call is finished n What if f calls g and g calls h, but calling h is the last thing g does (a tail call)? 9/5/17 85 9/5/17 86 An Important Optimization Tail Recursion Tail call h f n When a function call is made, the return address needs to be saved to the stack so we know to where to return when the call is finished n What if f calls g and g calls h, but calling h is the last thing g does (a tail call)? n Then h can return directly to f instead of g n A recursive program is tail recursive if all recursive calls are tail calls n Tail recursive programs may be optimized to be implemented as loops, thus removing the function call overhead for the recursive calls n Tail recursion generally requires extra accumulator arguments to pass partial results n May require an auxiliary function 9/5/17 87 9/5/17 88 Tail Recursion - Example # let rec rev_aux list revlist = match list with [ ] -> revlist x :: xs -> rev_aux xs (x::revlist);; val rev_aux : 'a list -> 'a list -> 'a list = <fun> # let rev list = rev_aux list [ ];; val rev : 'a list -> 'a list = <fun> n What is its running time? Comparison n poor_rev [1,2,3] = n (poor_rev [2,3]) @ [1] = n ((poor_rev [3]) @ [2]) @ [1] = n (((poor_rev [ ]) @ [3]) @ [2]) @ [1] = n (([ ] @ [3]) @ [2]) @ [1]) = n ([3] @ [2]) @ [1] = n (3:: ([ ] @ [2])) @ [1] = n [3,2] @ [1] = n 3 :: ([2] @ [1]) = n 3 :: (2:: ([ ] @ [1])) = [3, 2, 1] 9/5/17 89 9/5/17 90

Comparison n rev [1,2,3] = n rev_aux [1,2,3] [ ] = n rev_aux [2,3] [1] = n rev_aux [3] [2,1] = n rev_aux [ ] [3,2,1] = [3,2,1] Folding Functions over Lists How are the following functions similar? # let rec sumlist list = match list with [ ] -> 0 x::xs -> x + sumlist xs;; val sumlist : int list -> int = <fun> # sumlist [2;3;4];; - : int = 9 # let rec prodlist list = match list with [ ] -> 1 x::xs -> x * prodlist xs;; val prodlist : int list -> int = <fun> # prodlist [2;3;4];; - : int = 24 9/5/17 91 9/5/17 92 Folding # let rec fold_left f a list = match list with [] -> a (x :: xs) -> fold_left f (f a x) xs;; val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a = <fun> fold_left f a [x 1 ; x 2 ; ;x n ] = f( (f (f a x 1 ) x 2 ) )x n # let rec fold_right f list b = match list with [ ] -> b (x :: xs) -> f x (fold_right f xs b);; val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b = <fun> fold_right f [x 1 ; x 2 ; ;x n ] b = f x 1 (f x 2 ( (f x n b) )) Folding - Forward Recursion # let sumlist list = fold_right (+) list 0;; val sumlist : int list -> int = <fun> # sumlist [2;3;4];; - : int = 9 # let prodlist list = fold_right ( * ) list 1;; val prodlist : int list -> int = <fun> # prodlist [2;3;4];; - : int = 24 9/5/17 93 9/5/17 94 Folding - Tail Recursion - # let rev list = - fold_left - (fun l -> fun x -> x :: l) //comb op [] //accumulator cell list Folding n Can replace recursion by fold_right in any forward primitive recursive definition n Primitive recursive means it only recurses on immediate subcomponents of recursive data structure n Can replace recursion by fold_left in any tail primitive recursive definition 9/5/17 95 9/5/17 96