Tail Recursion.

Slides:



Advertisements
Similar presentations
1 Scheme and Functional Programming Aaron Bloomfield CS 415 Fall 2005.
Advertisements

Lists in Lisp and Scheme a. Lists are Lisp’s fundamental data structures, but there are others – Arrays, characters, strings, etc. – Common Lisp has moved.
Tail Recursion. Problems with Recursion Recursion is generally favored over iteration in Scheme and many other languages – It’s elegant, minimal, can.
1 Programming Languages (CS 550) Lecture Summary Functional Programming and Operational Semantics for Scheme Jeremy R. Johnson.
Lisp II. How EQUAL could be defined (defun equal (x y) ; this is how equal could be defined (cond ((numberp x) (= x y)) ((atom x) (eq x y)) ((atom y)
Functional Programming. Pure Functional Programming Computation is largely performed by applying functions to values. The value of an expression depends.
Recursion vs. Iteration The original Lisp language was truly a functional language: –Everything was expressed as functions –No local variables –No iteration.
Lisp II. How EQUAL could be defined (defun equal (x y) ; this is how equal could be defined (cond ((numberp x) (= x y)) ((atom x) (eq x y)) ((atom y)
Chapter 3 Functional Programming. Outline Introduction to functional programming Scheme: an untyped functional programming language.
Tail Recursion. Problems with Recursion Recursion is generally favored over iteration in Scheme and many other languages It’s elegant, minimal, can be.
Imperative programming public int factorial (int N){ int F = 1; for(X=N; X>1; X-- ){ F= F*X; } return F; } Functional programming (defun factorial(N) (cond.
>(setf oldlist ) Constructing a list We know how to make a list in lisp; we simply write it: ‘4321( ) What if we want a new list with that list as a part?
Functional programming: LISP Originally developed for symbolic computing First interactive, interpreted language Dynamic typing: values have types, variables.
Tail Recursion. Problems with Recursion Recursion is generally favored over iteration in Scheme and many other languages – It’s elegant, minimal, can.
Lisp and Scheme I. Versions of LISP LISP is an acronym for LISt Processing language Lisp is an old language with many variants – Fortran is the only older.
CS 152: Programming Language Paradigms February 24 Class Meeting Department of Computer Science San Jose State University Spring 2014 Instructor: Ron Mak.
224 3/30/98 CSE 143 Recursion [Sections 6.1, ]
Reading – Chapter 10. Recursion The process of solving a problem by reducing it to smaller versions of itself Example: Sierpinski’s TriangleSierpinski’s.
Streams and Lazy Evaluation in Lisp and Scheme. Overview Examples of using closures Delay and force Macros Different models of expression evaluation –
CS 326 Programming Languages, Concepts and Implementation Instructor: Mircea Nicolescu Lecture 8.
Functional Programming With examples in F#. Pure Functional Programming Functional programming involves evaluating expressions rather than executing commands.
Prolog Program Style (ch. 8) Many style issues are applicable to any program in any language. Many style issues are applicable to any program in any language.
CS535 Programming Languages Chapter - 10 Functional Programming With Lists.
Lisp and Scheme I. Versions of LISP LISP is an acronym for LISt Processing language Lisp is an old language with many variants – Fortran is the only older.
Chapter 15: Recursion. Objectives In this chapter, you will: – Learn about recursive definitions – Explore the base case and the general case of a recursive.
Chapter 9 Recursion © 2006 Pearson Education Inc., Upper Saddle River, NJ. All rights reserved.
Chapter 15: Recursion. Recursive Definitions Recursion: solving a problem by reducing it to smaller versions of itself – Provides a powerful way to solve.
Chapter 15: Recursion. Objectives In this chapter, you will: – Learn about recursive definitions – Explore the base case and the general case of a recursive.
Lisp and Scheme I. Versions of LISP LISP is an acronym for LISt Processing language Lisp is an old language with many variants – Fortran is only older.
CSC 143 P 1 CSC 143 Recursion [Chapter 5]. CSC 143 P 2 Recursion  A recursive definition is one which is defined in terms of itself  Example:  Compound.
Artificial Intelligence and Lisp Lecture 6 LiU Course TDDC65 Autumn Semester,
Programming with Recursion
CS314 – Section 5 Recitation 9
Lets and Loops Tail Recursion.
Functional Programming
Chapter 8: Recursion Data Structures in Java: From Abstract Data Types to the Java Collections Framework by Simon Gray.
Recursion CSE 2320 – Algorithms and Data Structures
CS 326 Programming Languages, Concepts and Implementation
CSE341: Programming Languages Lecture 6 Nested Patterns Exceptions Tail Recursion Dan Grossman Spring 2017.
CSE 341 Lecture 5 efficiency issues; tail recursion; print
CS 326 Programming Languages, Concepts and Implementation
Lists in Lisp and Scheme
Env. Model Implementation
6.001 SICP Stacks and recursion
6.001 SICP Data abstractions
Important Concepts from Clojure
Important Concepts from Clojure
Recursion Chapter 11.
Stacks & Recursion.
CSE341: Programming Languages Lecture 6 Nested Patterns Exceptions Tail Recursion Dan Grossman Spring 2013.
CS 36 – Chapter 11 Functional programming Features Practice
CSE341: Programming Languages Lecture 6 Nested Patterns Exceptions Tail Recursion Dan Grossman Autumn 2018.
Functions in Hmmm Assembly
Streams, Delayed Evaluation and a Normal Order Interpreter
Lisp and Scheme I.
Streams and Lazy Evaluation in Lisp and Scheme
Streams and Lazy Evaluation in Lisp and Scheme
6.001 SICP Explicit-control evaluator
CSE341: Programming Languages Lecture 6 Nested Patterns Exceptions Tail Recursion Zach Tatlock Winter 2018.
Clojure Macros.
Announcements Quiz 5 HW6 due October 23
Important Concepts from Clojure
Yan Shi CS/SE 2630 Lecture Notes
CSE341: Programming Languages Lecture 6 Nested Patterns Exceptions Tail Recursion Dan Grossman Spring 2016.
Rehearsal: Lazy Evaluation Infinite Streams in our lazy evaluator
CSE341: Programming Languages Lecture 6 Nested Patterns Exceptions Tail Recursion Dan Grossman Spring 2019.
More Scheme CS 331.
Defining Macros in Scheme
CSE341: Programming Languages Lecture 6 Nested Patterns Exceptions Tail Recursion Dan Grossman Autumn 2017.
Advanced Analysis of Algorithms
Presentation transcript:

Tail Recursion

Problems with Recursion Recursion is generally favored over iteration in Scheme and many other languages It’s elegant, minimal, can be implemented with regular functions and easier to analyze formally It can also be less efficient more functional calls and stack operations (context saving and restoration) Running out of stack space leads to failure deep recursion

Tail recursion is iteration Tail recursion is a pattern of use that can be compiled or interpreted as iteration, avoiding the inefficiencies A tail recursive function is one where every recursive call is the last thing done by the function before returning and thus produces the function’s value

Scheme’s top level loop Consider a simplified version of the REPL (define (repl) (printf “> “) (print (eval (read))) (repl)) This is an easy case: with no parameters there is not much context

Scheme’s top level loop 2 Consider a fancier REPL (define (repl) (repl1 0)) (define (repl1 n) (printf “~s> “ n) (print (eval (read))) (repl1 (add1 n))) This is only slightly harder: just modify the local variable n and start at the top

Scheme’s top level loop 3 There might be more than one tail recursive call (define (repl1 n) (printf “~s> “ n) (print (eval (read))) (if (= n 9) (repl1 0) (repl1 (add1 n)))) What’s important is that there’s nothing more to do in the function after the recursive calls

Two skills Distinguishing a trail recursive call from

Naïve recursive factorial (define (fact1 n) ;; naive recursive factorial (if (< n 1) 1 (* n (fact1 (sub1 n)))))

Tail recursive factorial (define (fact2 n) ; rewrite to just call the tail-recursive ; factorial with the appropriate initial values (fact2-helper n 1)) (define (fact2-helper n accumulator) ; tail recursive factorial calls itself as ; the last thing to be done (if (< n 1) accumulator (fact2-helper (sub1 n) (* accumulator n))))

Trace shows what’s going on > (require (lib "trace.ss")) > (load "fact.ss") > (trace fact1) > (fact1 6) |(fact1 6) | (fact1 5) | |(fact1 4) | | (fact1 3) | | |(fact1 2) | | | (fact1 1) | | | |(fact1 0) | | | |1 | | | 1 | | |2 | | 6 | |24 | 120 |720 720

fact2 > (trace fact2 fact2-helper) > (fact2 6) |(fact2 6) |(fact2-helper 6 1) |(fact2-helper 5 6) |(fact2-helper 4 30) |(fact2-helper 3 120) |(fact2-helper 2 360) |(fact2-helper 1 720) |(fact2-helper 0 720) |720 720 The interpreter and compiler notice that the last expression to be evaluated and returned in fact2-helper is a recursive call. Instead of pushing informa-tion on the sack, it reassigns the local variables and jumps to the beginning of the procedure. Thus, the recursion is auto-matically transformed into iteration.

Reverse a list This version works, but has two problems (define (rev1 list) ; returns the reverse a list (if (null? list) empty (append (rev1 (rest list)) (list (first list)))))) It is not tail recursive It creates needless temporary lists

A better reverse (define (rev2 list) (rev2.1 list empty)) (define (rev2.1 list reversed) (if (null? list) reversed (rev2.1 (rest list) (cons (first list) reversed))))

rev1 and rev2 > (load "reverse.ss") > (trace rev1 rev2 rev2.1) > (rev1 '(a b c)) |(rev1 (a b c)) | (rev1 (b c)) | |(rev1 (c)) | | (rev1 ()) | | () | |(c) | (c b) |(c b a) (c b a) > (rev2 '(a b c)) |(rev2 (a b c)) |(rev2.1 (a b c) ()) |(rev2.1 (b c) (a)) |(rev2.1 (c) (b a)) |(rev2.1 () (c b a)) |(c b a) (c b a) >

The other problem Append copies the top level list structure of it’s first argument. (append ‘(1 2 3) ‘(4 5 6)) creates a copy of the list (1 2 3) and changes the last cdr pointer to point to the list (4 5 6) In reverse, each time we add a new element to the end of the list, we are (re-)copying the list.

Append (two args only) (define (append list1 list2) (if (null? list1) list2 (cons (first list1) (append (rest list1) list2))))

Why does this matter? The repeated rebuilding of the reversed list is needless work It uses up memory and adds to the cost of garbage collection (GC) GC adds a significant overhead to the cost of any system that uses it Experienced Lisp programmers avoid algorithms that needlessly consume cons cells