+ - 0:00:00
Notes for current slide
Notes for next slide

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

CS450

Structure of Higher Level Languages

Lecture 4: Recursion, nested definitions

Tiago Cogumbreiro

1 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Exercises with lists

2 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

A template of recursion

Copying a list

(define (copy l)
(match l
[(list)
; input = '()
(list)
; expected output = '()
]
[(list h l ...) ; input = (list 1 2 3)
; h = 1
; l = (list 2 3)
(define result (copy l))
; result = (list 2 3)
(cons h result)
; expected output = (list 1 2 3)
]))

Notes

  • (define (copy l) l) would be a simpler solution, but we're trying to illustrate a recursion pattern.
  • Remember to write the expected inputs and outputs for a given example
  • Part in yellow: given the expected inputs, how would you build the expected output?
3 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Lists: example 1

Summation of all elements of a list

Spec

(require rackunit)
(check-equal? 10 (sum-list (list 1 2 3 4)))
(check-equal? 0 (sum-list (list)))
4 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Lists: example 1

Summation of all elements of a list

Solution

(define (sum l)
(match l
[(list) ; input = '()
0] ; expected output = 0
[(list h l ...) ; input = '(1 2 3 4)
; h = 1
; l = '(2 3 4)
; result = (sum (list 2 3 4)) = 2 + 3 + 4 = 9
(define result (sum l))
(+ h result)
])) ; expected output = (+ 1 9) = 10

Notes

  • We use the same pattern as in copy
  • Again, the expression being returned combines h and result
5 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Spot the error! (#1)

#lang racket
(define (sum-list l)
(match l
[empty 0]
[(list h l ...) (+ h (sum-list l))]))
6 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Spot the error! (#1)

#lang racket
(define (sum-list l)
(match l
[empty 0]
[(list h l ...) (+ h (sum-list l))]))
  • For match consider empty to be defined as (define empty (list)), not as a keyword
  • Pattern empty means: anything you find assign it to a variable called empty; same as writing [x 0]
  • The first branch matches with anything you give it, so this function never recurses
7 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Spot the error! (#2)

#lang racket
(define (sum-list l)
(match l
[(list) 0]
[(list h t ...) (+ h (sum-list l))]))
8 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Spot the error! (#2)

#lang racket
(define (sum-list l)
(match l
[(list) 0]
[(list h t ...) (+ h (sum-list l))]))
  • We wanted to recurse on t, but instead recursed on the original list l
  • This leads to an infinite loop
  • Good practice: use l as the rest of the list, and make this error impossible.
9 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Spot the error! (#3)

#lang racket
(define (sum-list l)
(match l
[(list) 0]
[(h l ...) (+ h (sum-list l))]))
10 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Spot the error! (#3)

#lang racket
(define (sum-list l)
(match l
[(list) 0]
[(h l ...) (+ h (sum-list l))]))
  • We forgot to specify the data-type list in the second pattern
  • Racket will raise an exception notifying us that the pattern is incorrect
ex.rkt:5:5: match: syntax error in pattern
in: (h l ...)
location...:
ex.rkt:5:5
11 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Lists: example 2

Returns a list from n down to 1

Spec

(require rackunit)
(check-equal? (list) (count-down 0))
(check-equal? (list 3 2 1) (count-down 3))
12 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Lists: example 2

Returns a list from n down to 1

Spec

(require rackunit)
(check-equal? (list) (count-down 0))
(check-equal? (list 3 2 1) (count-down 3))

Solution

#lang racket
(define (count-down n)
(cond [(<= n 0) (list)]
[else (cons n (count-down (- n 1)))]))
12 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Lists: example 3

Point-wise pairing of two lists

Spec

(require rackunit)
(check-equal? (list (cons 3 30) (cons 2 20) (cons 1 10))
(zip (list 3 2 1) (list 30 20 10)))
(check-equal? (list (cons 3 30) (cons 2 20) (cons 1 10))
(zip (list 3 2 1) (list 30 20 10 5 4 3 2 1)))
(check-equal? (list (cons 3 30) (cons 2 20) (cons 1 10))
(zip (list 3 2 1 90 180 270) (list 30 20 10)))
13 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Lists: example 3

Point-wise pairing of two lists

14 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Lists: example 3

Point-wise pairing of two lists

Solution

#lang racket
(define pair list) ; Encode a pair as a list
(define (zip l1 l2)
14 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Lists: example 3

Point-wise pairing of two lists

Solution

#lang racket
(define pair list) ; Encode a pair as a list
(define (zip l1 l2)

(match* (l1 l2)
[((list) _) (list)]
[(_ (list)) (list)]
[((list h1 l1 ...) (list h2 l2 ...))
(cons
(pair h1 h2)
(zip l1 l2))]))
  • Use match* to pattern match two values at once
14 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Using nested definitions

15 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Build a list from 1 up to n

Our goal is to build a list from 1 up to some number. Here is a template of our function and a test case for us to play with. For the sake of simplicity, we will not handle non-positive numbers.

#lang racket
(define (countup-from1 x) #f)
(require rackunit)
(check-equal? (list 1) (countup-from1 1))
(check-equal? (list 1 2) (countup-from1 2))
(check-equal? (list 1 2 3 4 5) (countup-from1 5))

Hint: write a helper function count that builds counts from n up to m.

16 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Exercise 1: attempt #1

We write a helper function count that builds counts from n up to m.

#lang racket
(define (countup-from1 x)
(count 1 x))
(define (count from to)
(cond
[(equal? from to) (list to)]
[else (cons from (count (+ 1 from) to))]))
17 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Exercise 1: attempt #1

We write a helper function count that builds counts from n up to m.

#lang racket
(define (countup-from1 x)
(count 1 x))
(define (count from to)
(cond
[(equal? from to) (list to)]
[else (cons from (count (+ 1 from) to))]))

Let us refactor the code and hide function count

17 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Exercise 1: attempt #2

We move function count to be internal to function countup-from1, as it is a helper function and therefore it is good practice to make it private to countup-from1.

(define (countup-from1 x)
; Internally defined function, not visible from
; the outside
(define (count from to)
(cond [(equal? from to) (list to)]
[else (cons from (count (+ 1 from) to))]))
; The same call as before
(count 1 x))
18 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

When to nest functions?

Nest functions:

  • If they are unnecessary outside
  • If they are under development
  • If you want to hide them: Every function in the public interface of your code is something you'll have to maintain!
19 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Intermission:

Nested definitions

20 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Nested definition: local variables

Nested definitions bind a variable within the body of a function and are only visible within that function (these are local variables)

#lang racket
(define (f x)
(define z 3)
(+ x z))
(+ 1 z) ; Error: z is not visible outside function f
21 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Nested definitions shadow other variables

Nested definitions silently shadow any already defined variable

#lang racket
(define z 10)
(define (f x)
(define x 3) ; Shadows parameter
(define z 20) ; Shadows global
(+ x z))
(f 1) ; Outputs 23
22 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

No redefined local variables

It is an error to re-define local variables

#lang racket
(define (f b)
; OK to shadow a parameter
(define b (+ b 1))
(define a 1)
; Not OK to re-define local variables
; Error: define-values: duplicate binding name
(define a (+ a 1))
(+ a b))
23 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Back to Exercise 1

24 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Exercise 1: attempt #2

Notice that we have some redundancy in our code. In function count, parameter to remains unchanged throughout execution.

(define (countup-from1 x)
; Internally defined function, not visible from
; the outside
(define (count from to)
(cond [(equal? from to) (list to)]
[else (cons from (count (+ 1 from) to))]))
; The same call as before
(count 1 x))
25 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Exercise 1: attempt #3

We removed parameter to from function count as it was constant throughout the execution. Variable to is captured/copied when count is defined.

(define (countup-from1 to)
; Internally defined function, not visible from
; the outside
(define (count from)
(cond [(equal? from to) (list to)]
[else (cons from (count (+ 1 from)))]))
; The same call as before
(count 1))
26 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Example 1: summary

  • Use a nested definition to hide a function that is only used internally.
  • Nested definitions can refer to variables defined outside the scope of their definitions.
  • The last expression of a function's body is evaluated as the function's return value
27 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Measuring performance

28 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Example 2

Maximum number from a list of integers

29 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Example 2: attempt 1

Finding the maximum element of a list.

#lang racket
(define (max xs)
(cond
[(empty? xs) (error "max: expecting a non-empty list!")]
[(empty? (rest xs)) (first xs)] ; The list only has one element (the max)
[(> (first xs) (max (rest xs))) (first xs)] ; The max of the rest is smaller than 1st
[else (max (rest xs))])) ; Otherwise, use the max of the rest
; A simple unit-test
(require rackunit)
(check-equal? 10 (max (list 1 2 10 4 0)))

We use function error to abort the program with an exception.

30 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Example 2: attempt 1

Finding the maximum element of a list.

Let us benchmark max with sorted list (worst-case scenario):

  • 20 elements: 18.43ms
  • 21 elements: 36.63ms
  • 22 elements: 75.78ms

Whenever we add an element we double the execution time. Why?

31 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Example 2: attempt 1

Whenever we hit the else branch (because we can't find the maximum), we re-compute the max element.

(define (max xs)
(cond
[(empty? xs) (error "max: expecting a non-empty list!")]
[(empty? (rest xs)) (first xs)] ; The list only has one element (the max)
[(> (first xs) (max (rest xs))) (first xs)] ; The max of the rest is smaller than 1st
[else (max (rest xs))])) ; Otherwise, use the max of the rest
32 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Example 2: attempt 2

We use a local variable to cache a duplicate computation.

(define (max xs)
(cond
[(empty? xs) (error "max: expecting a non-empty list!")]
[(empty? (rest xs)) (first xs)]
[else
(define rest-max (max (rest xs))) ; Cache the max of the rest
(cond
[(> (first xs) rest-max) (first xs)]
[else rest-max])]))
  • Attempt #1: 20 elements in 75.78ms
  • Attempt #2: 1,000,000 elements in 101.15ms
33 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Example 2 takeaways

  • Use nested definitions to cache intermediate results
  • Identify repeated computations and cache them in nested (local) definitions
34 / 34

CS450  ☽  Recursion, nested definitions  ☽  Lecture 4  ☽  Tiago Cogumbreiro

Exercises with lists

2 / 34
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow