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

Similar documents
CSci 4223 Principles of Programming Languages

Tail Recursion and Accumulators

CS Lecture 5: Pattern Matching. Prof. Clarkson Fall Today s music: Puff, the Magic Dragon by Peter, Paul & Mary

Function definitions. CSE341: Programming Languages Lecture 2 Functions, Pairs, Lists. Example, extended. Some gotchas. Zach Tatlock Winter 2018

CSE341: Programming Languages Lecture 2 Functions, Pairs, Lists. Zach Tatlock Winter 2018

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

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

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

CSE341: Programming Languages Lecture 2 Functions, Pairs, Lists. Dan Grossman Fall 2011

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

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

CSE 341 : Programming Languages

FUNCTIONAL PROGRAMMING

CSE 341: Programming Languages

Lists. Prof. Clarkson Fall Today s music: "Blank Space" by Taylor Swift

CSE341: Programming Languages Lecture 11 Type Inference. Dan Grossman Spring 2016

Standard ML. Exceptions

CSE341: Programming Languages Lecture 4 Records, Datatypes, Case Expressions. Dan Grossman Spring 2013

CSE 341: Programming Languages

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

CSE 341 : Programming Languages Midterm, Spring 2015

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

SML Style Guide. Last Revised: 31st August 2011

Begin at the beginning

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

CS 11 Ocaml track: lecture 2

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

Part I: Written Problems

CSE 341: Programming Languages

CS558 Programming Languages

Introduction to OCaml

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

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

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

15 212: Principles of Programming. Some Notes on Continuations

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

CSE 341 Section 5. Winter 2018

A Second Look At ML. Chapter Seven Modern Programming Languages, 2nd ed. 1

Programming Languages

Topic 5: Examples and higher-order functions

Programming Languages

G Programming Languages - Fall 2012

Programming Languages and Compilers (CS 421)

SML A F unctional Functional Language Language Lecture 19

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

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

CSE341 Spring 2016, Midterm Examination April 29, 2016

An introduction introduction to functional functional programming programming using usin Haskell

CS152: Programming Languages. Lecture 11 STLC Extensions and Related Topics. Dan Grossman Spring 2011

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

Recap: Functions as first-class values

Programming Languages

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

CS422 - Programming Language Design

CSC324- TUTORIAL 5. Shems Saleh* *Some slides inspired by/based on Afsaneh Fazly s slides

CSE341, Fall 2011, Midterm Examination October 31, 2011

Programming Languages

Programming Languages and Compilers (CS 421)

CSE3322 Programming Languages and Implementation

CSE-505: Programming Languages. Lecture 20.5 Recursive Types. Zach Tatlock 2016

CSE341 Section 3. Standard-Library Docs, First-Class Functions, & More

Programming Languages

Continuations and Continuation-Passing Style

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

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

Programming Languages

Programming Languages

CSCC24 Functional Programming Scheme Part 2

Lecture 5: Declarative Programming. The Declarative Kernel Language Machine. September 12th, 2011

CSE341 Spring 2016, Midterm Examination April 29, 2016

Handout 2 August 25, 2008

Programming Languages and Compilers (CS 421)

The Typed Racket Guide

Recap from last time. Programming Languages. CSE 130 : Fall Lecture 3: Data Types. Put it together: a filter function

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

CS Lecture 7: The dynamic environment. Prof. Clarkson Spring Today s music: Down to Earth by Peter Gabriel from the WALL-E soundtrack

Parsing Scheme (+ (* 2 3) 1) * 1

CSc 520. Gofer III. Accumulative Recursion. Accumulative Recursion... Stack Recursion. Principles of Programming Languages. Christian Collberg

Programming Languages

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

CSE341, Fall 2011, Midterm Examination October 31, 2011

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

Functional Programming. Pure Functional Programming

CSE 130 [Winter 2014] Programming Languages

Variables and Bindings

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

Scope, Functions, and Storage Management

Quick announcement. Midterm date is Wednesday Oct 24, 11-12pm.

Fall 2018 Lecture 1

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

Typed Racket: Racket with Static Types

Lists. Michael P. Fourman. February 2, 2010

CSE399: Advanced Programming. Handout 2

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

COSE212: Programming Languages. Lecture 4 Recursive and Higher-Order Programming

Informatics 1 Functional Programming Lecture 4. Lists and Recursion. Don Sannella University of Edinburgh

INF 212 ANALYSIS OF PROG. LANGS FUNCTION COMPOSITION. Instructors: Crista Lopes Copyright Instructors.

Local definitions and lexical scope

Local definitions and lexical scope. Local definitions. Motivating local definitions. s(s a)(s b)(s c), where s = (a + b + c)/2.

Transcription:

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

Nested patterns We can nest patterns as deep as we want Just like we can nest expressions as deep as we want Often avoids hard-to-read, wordy nested case expressions So the full meaning of pattern-matching is to compare a pattern against a value for the same shape and bind variables to the right parts More precise recursive definition coming after examples 2

Useful example: zip/unzip 3 lists fun zip3 lists = case lists of ([],[],[]) => [] (hd1::tl1,hd2::tl2,hd3::tl3) => (hd1,hd2,hd3)::zip3(tl1,tl2,tl3) _ => raise ListLengthMismatch fun unzip3 triples = case triples of [] => ([],[],[]) (a,b,c)::tl => let val (l1, l2, l3) = unzip3 tl in (a::l1,b::l2,c::l3) end More examples in.sml files 3

Style Nested patterns can lead to very elegant, concise code Avoid nested case expressions if nested patterns are simpler and avoid unnecessary branches or let-expressions Example: unzip3 and nondecreasing A common idiom is matching against a tuple of datatypes to compare them Examples: zip3 and multsign Wildcards are good style: use them instead of variables when you do not need the data Examples: len and multsign 4

(Most of) the full definition The semantics for pattern-matching takes a pattern p and a value v and decides (1) does it match and (2) if so, what variable bindings are introduced. Since patterns can nest, the definition is elegantly recursive, with a separate rule for each kind of pattern. Some of the rules: If p is a variable x, the match succeeds and x is bound to v If p is _, the match succeeds and no bindings are introduced If p is (p1,,pn) and v is (v1,,vn), the match succeeds if and only if p1 matches v1,, pn matches vn. The bindings are the union of all bindings from the submatches If p is C p1, the match succeeds if v is C v1 (i.e., the same constructor) and p1 matches v1. The bindings are the bindings from the submatch. (there are several other similar forms of patterns) 5

Examples Pattern a::b::c::d matches all lists with >= 3 elements Pattern a::b::c::[] matches all lists with 3 elements Pattern ((a,b),(c,d))::e matches all non-empty lists of pairs of pairs 6

Exceptions An exception binding introduces a new kind of exception exception MyUndesirableCondition exception MyOtherException of int * int The raise primitive raises (a.k.a. throws) an exception raise MyUndesirableException raise (MyOtherException (7,9)) A handle expression can handle (a.k.a. catch) an exception If doesn t match, exception continues to propagate e1 handle MyUndesirableException => e2 e1 handle MyOtherException(x,y) => e2 7

Actually Exceptions are a lot like datatype constructors Declaring an exception adds a constructor for type exn Can pass values of exn anywhere (e.g., function arguments) Not too common to do this but can be useful handle can have multiple branches with patterns for type exn 8

Recursion Should now be comfortable with recursion: No harder than using a loop (whatever that is J) Often much easier than a loop When processing a tree (e.g., evaluate an arithmetic expression) Examples like appending lists Avoids mutation even for local variables Now: How to reason about efficiency of recursion The importance of tail recursion Using an accumulator to achieve tail recursion [No new language features here] 9

Call-stacks While a program runs, there is a call stack of function calls that have started but not yet returned Calling a function f pushes an instance of f on the stack When a call to f finishes, it is popped from the stack These stack-frames store information like the value of local variables and what is left to do in the function Due to recursion, multiple stack-frames may be calls to the same function 10

Example fun fact n = if n=0 then 1 else n*fact(n-1) val x = fact 3 fact 3 fact 3: 3*_ fact 2 fact 3: 3*_ fact 3: 3*_ fact 2: 2*_ fact 2: 2*_ fact 1 fact 1: 1*_ fact 0 fact 3: 3*_ fact 3: 3*_ fact 3: 3*_ fact 3: 3*2 fact 2: 2*_ fact 2: 2*_ fact 2: 2*1 fact 1: 1*_ fact 1: 1*1 fact 0: 1 11

Example Revised fun fact n = let fun aux(n,acc) = if n=0 then acc else aux(n-1,acc*n) in aux(n,1) end val x = fact 3 Still recursive, more complicated, but the result of recursive calls is the result for the caller (no remaining multiplication) 12

The call-stacks fact 3 fact 3: _ fact 3: _ fact 3: _ aux(3,1) aux(3,1):_ aux(3,1):_ aux(2,3) aux(2,3):_ aux(1,6) fact 3: _ fact 3: _ fact 3: _ fact 3: _ aux(3,1):_ aux(3,1):_ aux(3,1):_ aux(3,1):_ aux(2,3):_ aux(2,3):_ aux(2,3):_ aux(2,3):6 aux(1,6):_ aux(0,6) aux(1,6):_ aux(0,6):6 aux(1,6):6 Etc 13

An optimization It is unnecessary to keep around a stack-frame just so it can get a callee s result and return it without any further evaluation ML recognizes these tail calls in the compiler and treats them differently: Pop the caller before the call, allowing callee to reuse the same stack space (Along with other optimizations,) as efficient as a loop Reasonable to assume all functional-language implementations do tail-call optimization 14

What really happens fun fact n = let fun aux(n,acc) = if n=0 then acc else aux(n-1,acc*n) in aux(n,1) end val x = fact 3 fact 3 aux(3,1) aux(2,3) aux(1,6) aux(0,6) 15

Moral of tail recursion Where reasonably elegant, feasible, and important, rewriting functions to be tail-recursive can be much more efficient Tail-recursive: recursive calls are tail-calls There is a methodology that can often guide this transformation: Create a helper function that takes an accumulator Old base case becomes initial accumulator New base case becomes final accumulator 16

Methodology already seen fun fact n = let fun aux(n,acc) = if n=0 then acc else aux(n-1,acc*n) in aux(n,1) end val x = fact 3 fact 3 aux(3,1) aux(2,3) aux(1,6) aux(0,6) 17

Another example fun sum xs = case xs of [] => 0 x::xs => x + sum xs fun sum xs = let fun aux(xs,acc) = case xs of [] => acc x::xs => aux(xs,x+acc) in aux(xs,0) end 18

And another fun rev xs = case xs of [] => [] x::xs => (rev xs ) @ [x] fun rev xs = let fun aux(xs,acc) = case xs of [] => acc x::xs => aux(xs,x::acc) in aux(xs,[]) end 19

Actually much better fun rev xs = case xs of [] => [] x::xs => (rev xs ) @ [x] For fact and sum, tail-recursion is faster but both ways linear time Non-tail recursive rev is quadratic because each recursive call uses append, which must traverse the first list And 1+2+ +(length-1) is almost length*length/2 Moral: beware list-append, especially within outer recursion Cons constant-time (and fast), so accumulator version much better 20

Always tail-recursive? There are certainly cases where recursive functions cannot be evaluated in a constant amount of space Most obvious examples are functions that process trees In these cases, the natural recursive approach is the way to go You could get one recursive call to be a tail call, but rarely worth the complication Also beware the wrath of premature optimization Favor clear, concise code But do use less space if inputs may be large 21

What is a tail-call? The nothing left for caller to do intuition usually suffices If the result of f x is the immediate result for the enclosing function body, then f x is a tail call But we can define tail position recursively Then a tail call is a function call in tail position 22

Precise definition A tail call is a function call in tail position If an expression is not in tail position, then no subexpressions are In fun f p = e, the body e is in tail position If if e1 then e2 else e3 is in tail position, then e2 and e3 are in tail position (but e1 is not). (Similar for case-expressions) If let b1 bn in e end is in tail position, then e is in tail position (but no binding expressions are) Function-call arguments e1 e2 are not in tail position 23