Hints for Exercise 4: Recursion

Similar documents
6.001 Notes: Section 8.1

6.001 Notes: Section 6.1

Week - 03 Lecture - 18 Recursion. For the last lecture of this week, we will look at recursive functions. (Refer Slide Time: 00:05)

(Refer Slide Time: 05:25)

Animations involving numbers

Types of recursion. Readings: none. In this module: a glimpse of non-structural recursion. CS 135 Winter : Types of recursion 1

Intro. Scheme Basics. scm> 5 5. scm>

CS 135 Winter 2018 Tutorial 7: Accumulative Recursion and Binary Trees. CS 135 Winter 2018 Tutorial 7: Accumulative Recursion and Binary Trees 1

CSCC24 Functional Programming Scheme Part 2

Chapter 10 Recursion

CS115 - Module 10 - General Trees

1 Lecture 5: Advanced Data Structures

Types of recursion. Structural vs. general recursion. Pure structural recursion. Readings: none. In this module: learn to use accumulative recursion

Statistics Case Study 2000 M. J. Clancy and M. C. Linn

Announcements. The current topic: Scheme. Review: BST functions. Review: Representing trees in Scheme. Reminder: Lab 2 is due on Monday at 10:30 am.

6.001 Notes: Section 4.1

Lab 6 Vectors and functions

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

Design and Analysis of Algorithms Prof. Madhavan Mukund Chennai Mathematical Institute. Week 02 Module 06 Lecture - 14 Merge Sort: Analysis

CSE200 Lecture 6: RECURSION

SCHEME 10 COMPUTER SCIENCE 61A. July 26, Warm Up: Conditional Expressions. 1. What does Scheme print? scm> (if (or #t (/ 1 0)) 1 (/ 1 0))

Repetition Through Recursion


The Art of Recursion: Problem Set 10

Intro. Speed V Growth

Lecture 6: Sequential Sorting

Design and Analysis of Algorithms Prof. Madhavan Mukund Chennai Mathematical Institute. Module 02 Lecture - 45 Memoization

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

Module 9: Trees. If you have not already, make sure you. Read How to Design Programs Sections 14, 15, CS 115 Module 9: Trees

9. The Disorganized Handyman

Programming Paradigms

More About Recursive Data Types

(Refer Slide Time: 06:01)

6.001 Notes: Section 17.5

6.001 Notes: Section 15.1

CSCI 1100L: Topics in Computing Lab Lab 11: Programming with Scratch

Binary Search Trees. Carlos Moreno uwaterloo.ca EIT

n! = 1 * 2 * 3 * 4 * * (n-1) * n

LOOPS. Repetition using the while statement

PROFESSOR: Last time, we took a look at an explicit control evaluator for Lisp, and that bridged the gap between

CS 564 PS1. September 10, 2017

Computer Programming. Basic Control Flow - Loops. Adapted from C++ for Everyone and Big C++ by Cay Horstmann, John Wiley & Sons

Notes - Recursion. A geeky definition of recursion is as follows: Recursion see Recursion.

COMP-202: Foundations of Programming. Lecture 26: Review; Wrap-Up Jackie Cheung, Winter 2016

University of Washington CSE 140 Data Programming Winter Final exam. March 11, 2013

TAIL CALLS, ITERATORS, AND GENERATORS 11

Lab 10: OCaml sequences, comparators, stable sorting 12:00 PM, Nov 12, 2017

CS103 Handout 29 Winter 2018 February 9, 2018 Inductive Proofwriting Checklist

Neo4J: Graph Database

I2204 ImperativeProgramming Semester: 1 Academic Year: 2018/2019 Credits: 5 Dr Antoun Yaacoub

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

COMP 250 Fall recurrences 2 Oct. 13, 2017

Reversing. Time to get with the program

FLOATING POINT NUMBERS

Week - 01 Lecture - 03 Euclid's Algorithm for gcd. Let us continue with our running example of gcd to explore more issues involved with program.

Lecture 4: examples of topological spaces, coarser and finer topologies, bases and closed sets

RECURSION, RECURSION, (TREE) RECURSION! 2

Functional Programming in Haskell for A level teachers

Computing Fundamentals Advanced functions & Recursion

Programming and Data Structures Prof. N.S. Narayanaswamy Department of Computer Science and Engineering Indian Institute of Technology, Madras

CS116 - Module 5 - Accumulative Recursion

CS52 - Assignment 8. Due Friday 4/15 at 5:00pm.

SCHEME AND CALCULATOR 5b

Foundations, Reasoning About Algorithms, and Design By Contract CMPSC 122

UNIVERSITY OF CALIFORNIA, SANTA CRUZ BOARD OF STUDIES IN COMPUTER ENGINEERING

Project and Production Management Prof. Arun Kanda Department of Mechanical Engineering Indian Institute of Technology, Delhi

Racket Style Guide Fall 2017

Linked Lists. What is a Linked List?

Project 5 - The Meta-Circular Evaluator

2. Click on the Freeform Pen Tool. It looks like the image to the right. If it s not showing, right click on that square and choose it from the list.

Lecture 11: Testing and Design Statistical Computing, Wednesday October 21, 2015

About these slides Prolog programming hints

Module 10A Lecture - 20 What is a function? Why use functions Example: power (base, n)

Week - 04 Lecture - 01 Merge Sort. (Refer Slide Time: 00:02)

Week 5 Tutorial Structural Induction

More Complicated Recursion CMPSC 122

RECURSION. Week 6 Laboratory for Introduction to Programming and Algorithms Uwe R. Zimmer based on material by James Barker. Pre-Laboratory Checklist

ENCM 501 Winter 2016 Assignment 1 for the Week of January 25

CS 3 Midterm 1 Review

COMP-202 Unit 4: Programming with Iterations

Earthwork 3D for Dummies Doing a digitized dirt takeoff calculation the swift and easy way

Lecture 18 Restoring Invariants

CSE 2123 Recursion. Jeremy Morris

(Provisional) Lecture 22: Rackette Overview, Binary Tree Analysis 10:00 AM, Oct 27, 2017

Is the statement sufficient? If both x and y are odd, is xy odd? 1) xy 2 < 0. Odds & Evens. Positives & Negatives. Answer: Yes, xy is odd

Lab 1 Implementing a Simon Says Game

Excel Basics: Working with Spreadsheets

1 Dynamic Memory continued: Memory Leaks

Class Structure. Prerequisites

Upgrading Your Geant4 Release

Lab 1 Implementing a Simon Says Game

PAIRS AND LISTS 6. GEORGE WANG Department of Electrical Engineering and Computer Sciences University of California, Berkeley

CS115 - Module 9 - filter, map, and friends

Excerpt from "Art of Problem Solving Volume 1: the Basics" 2014 AoPS Inc.

Recursively Enumerable Languages, Turing Machines, and Decidability

RECURSION, RECURSION, (TREE) RECURSION! 3

Assignment III: Graphing Calculator

Discussion 1H Notes (Week 3, April 14) TA: Brian Choi Section Webpage:

Using Arithmetic of Real Numbers to Explore Limits and Continuity

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

Transcription:

Hints for Exercise 4: Recursion EECS 111, Winter 2017 Due Wednesday, Jan 8th by 11:59pm Question 1: multiply-list For many list problems, this one included, the base case is when the list is empty, which you can check using (empty? <List>). Remember that you can divide a list into a head and tail using first and rest, respectively. first will return the first element, and rest returns a new, shorter list. So for instance, (define lon (list 1 2 3)) (first lon) ; 1 (rest lon) ; (list 2 3) (first (list 1)) ; 1 (rest (list 1)) ; empty ; `lon` = "list of numbers" Look back at the slides for definitions of recursive procedures if you re having trouble thinking of how to approach this problem. Question 2: iterated-overlay Recall that if some-generator is a function number -> image, then (iterated-overlay some-generator 3) is equivalent to saying (overlay (some-generator 0) (some-generator 1) (some-generator 2)) which, in turn, is equivalent to saying (overlay (some-generator 0) (overlay (some-generator 1) (some-generator 2))) This is a bit hard to read with the nesting, but all we re doing here is saying (overlay (some-generator 0) <BottomImage>) ; top image ;; where <BottomImage>, in turn, is (overlay (some-generator 1) ; top image (some-generator 2)) ; bottom image 1

Question 3: iterated-any This is just an abstraction of iterated-overlay. It might be helpful to go back to Exercise 2, and think about the process you used to abstract between the last two bullseye functions. You should follow the same workflow here. Going from iterated-overlay to iterated-any is a lot like going from count-odd? to count in this week s tutorial. Recall that iterated-overlay, iterated-beside, iterated-above, etc. all perform the same general task: they call a generator some number of times, and then combine the resulting images into a single return value. The name of each function specifies the combiner used: ;; square-gen : number -> image (define (square-gen n) (square (* n 10) "solid" (color (* n 50) 0 0))) (check-expect (iterated-beside square-gen 3) ;; equivalent to using `beside` to combine ;; all of the iteration results (beside (square-gen 0) (square-gen 1) (square-gen 2))) Questions 4 through 6: count-tree, sum-tree, max-tree All three of these functions have very similar structures. The data definition for a ListTree provides hints regarding the structure of the function. Once again: ;; A ListTree is one of: ;; - <Number> <-- this is your base case ;; - (list <ListTree>) <-- this is your recursive case Remember that you can use the predicates number? and list? to check if something is a number or a list, respectively. It might help to first write each of these functions for a single-layer list (i.e. no nested list hell), just to make sure you understand the general concept of recursively counting, summing, computing the max, etc. Namely, suppose we are writing a recursive function <DataType> -> <ReturnType>. Recall that the general pattern for a recursive step looks like this: ;; <Combiner> is any function of the form ;; <ReturnType>, <ReturnType> -> <ReturnType> (<Combiner>...something with the first part of <DataType>......recursive call with the rest of <DataType>...) Since we re working primarily with numbers here, some combiners to consider are the arithmetic operations +, *, -, etc. as well as the function max. Look up the documentation for max if you don t know what it does. Question 7: depth-tree This one is hard! Previous questions have asked you to implement functions using either map, foldr, etc. or recursion. It s true that map and co. are implemented using recursion under the hood (they abstract over the process of manually recurring on a list). However, this question is designed to show you that higher-order 2

functions and recursion can work together in fact, many types of problems like this one are much easier if you use both. To break this question down, it s crucial to understand (1) how the problem decomposes, and (2) how each of the higher-order functions is used. Let s start with decomposing the problem. Returning to our trusty data definition for ListTree: ;; A ListTree is one of: ;; - <Number> <-- this is your base case ;; - (list <ListTree>) <-- this is your recursive case So for any given ListTree, we have the following cases: It s just a single number. (What should we return?) It s a list of numbers. (What should we return?) It s a list of (a list of numbers)... Now things start to get tricky. But whenever you see a recursive data structure like this, it s a huge sign you should be using recursion to solve the problem, so there s probably going to be a recursive call in here somewhere. The main difference is that before, paring down our data divided things neatly into two parts. For a number, there s n and (- n 1). For a one-dimensional list, there s (first l) and (rest l). But for a TreeList, we can t just break things up this way. Consider the following example: (define my-tree (list (list 1 2) (list 1 (list 2)))) Right off the bat, since my-tree is a list, we know the depth is going to be at least 1. The question is, how do we account for the children? (first my-tree) gives us (list 1 2), which itself has a depth of 1. (rest my-tree) gives us (list 1 (list 2)), which itself has a depth of 2. Clearly, we want to add the larger of these two depths. What function might we use to compute this value? But we re not quite done! Suppose we have even more items: (define my-tree (list (list 1 2) (list 1 (list 2)) ;; Three new friends! 1 (list 1 (list 2 (list 3))) (list 1))) Does your answer still work for an arbitrary number of list items? We need some way of retrieving the largest depth out of an arbitrary number of list elements. Actually, we need some way to get the depth of all those elements to begin with. This leaves us with two questions: Which higher order functions are good at applying some function to all the items in a list? which higher order functions are good at aggregating a list of results into a single result? 3

Iterative Recursion Appendix 1: Iterative Recursion For instance, ;; fact : number -> number ;; regular factorial with ordinary recursion (define (fact n) 1 (* ; combiner n ; current data (- n 1)))) ; rest of our data (fact 5) ; call our function ;; fact/iter : number, number -> number ;; factorial using iterative recursion (define (fact/iter n acc) ; [1] acc ; [2] (fact/iter ; [3] (- n 1) (* n acc)))) (fact/iter 5 1) ; 1 is our starting accumulator There are exactly three differences to note here, labeled as comments in the code above: 1. The addition of a new argument for the accumulator (the partial product, in this case). This is reflected in: 1. An updated function header, (define (fact/iter n acc)...) 2. An updated recursive call, which now takes two arguments: the decremented number, and the new accumulator. 2. The base case now returns the accumulator rather than 1. That s because the base case signals we re done recurring, so the partial product is really the complete product at that point, and we can just return whatever value it s accrued. 3. All of our computations have moved inside the recursive call. To be clear, ;; ordinary recursion (* (<Combiner> n <Current> (fact (- n 1))) (<RecursiveCall> <Rest>)) ;; iterative recursion (fact/iter (<RecursiveCall> (- n 1) <Rest> (* n acc)) (<Combiner> <Current> <OldAcc>)) By making our combiner work on the accumulator, we can progress directly into the next recursion, without waiting for the recursive result to finish expanding. In other words, we don t need to leave 4

behind an expanding trail while we wait for the base case to hit: ;; 5! in progress, without accumulators (* 5 (* 4 (* 3 ; this trail gets big really quickly (fact (- 3 1))))) ;; 5! in progress, with accumulators (fact/iter (- 3 1) (* 3 20)) ; 20 is the accumulator 5 * 4 There s one last caveat, though. Notice that instead of calling our function with (fact 5) we now have to call it with 1 as our starting accumulator: (fact 5 1) This is really annoying and also bad practice from a software design standpoint: if the starting accumulator is always going to be 1, we should just write that into our software somehow. Leaving it to user discretion only increases the risk of something going wrong (e.g. suppose someone accidentally writes 0, and now fact/iter always returns 0 for every input). So, the final step in converting a function to iterative recursion is to provide a wrapper of some kind around your main recursive function. This wrapper should have the same signature as your main function, without the accumulator, and it should kick off the recursive process by calling your main function with the starting accumulator value. You can accomplish this in one of two ways: by writing a separate wrapper function, or using local as a wrapper. 1. Writing a separate wrapper function ;; 1. Rename our old function to fact/iter-helper ;; fact/iter-helper : number, number -> number ;; factorial using iterative recursion (define (fact/iter-helper n acc) acc (fact/iter-helper (- n 1) (* n acc)))) ;; 2. Make fact/iter a wrapper around the helper ;; fact/iter : number -> number (define (fact/iter n) ; notice acc is gone again (fact/iter-helper n 1)) ;; Now we can use our iterative solution like before! (fact/iter 5) 2. Using a local expression ;; Rename the original function to fact/iter-helper, and ;; dump it in the definitions part of a `local`. 5

;; fact/iter : number -> number (define (fact/iter n) (local [;; fact/iter-helper : number, number -> number ;; factorial using iterative recursion (define (fact/iter-helper n acc) acc (fact/iter-helper (- n 1) (* n acc))))] ;; Now that we've locally defined our helper, we can ;; just call it in the body of the `local` like normal. (fact/iter-helper n 1))) So, to summarize, there are four steps involved with the transformation from ordinary recursion to iterative recursion: 1. Add the accumulator argument to the function definition, and update the recursive call 2. Return the accumulator in the base case 3. Restructure the recursive step so that the recursive call is on the outside. Remember to pass in a new accumulator, which should be the result of combining the current data with the old accumulator. 4. Finally, wrap everything up so the function has the same signature as before you added the new argument. This means using a local or writing a helper function; both are perfectly reasonable strategies. 6