Lecture 13
Recursive Data Structures
Logistics and Week Ahead
For some - Ethics Module 5 - Identity
Reminders:
A quick aside: quoting lists
Note: I'm going to use quoting to simplify the following slides, it's never required or necessary to quote.
Warning: this works with CONSTANTS but provides strange behavior with more complex expressions.
A new function: cons
; cons : T (listof T) -> "(listof T)"
(cons element list)
> (cons 1 (list 2 3))�'(1 2 3)
> (cons 1� (cons 2
(list 3 4 5)))
'(1 2 3 4 5)
> (cons 1� (cons 2� (cons 3 '())))�'(1 2 3)�
Implementing map using cons
(define (my-map f a-list)
(if (empty? a-list)
'() ; remember '() means the empty list
(cons (f (first a-list))
(my-map f (rest a-list)))))
(check-expect (my-map - '(1 2 3 4 5))
(map - '(1 2 3 4 5)))
Running it - abbreviated substitution model
(my-map - '(1 2 3))
(my-map -
(rest '(1 2 3))))
(my-map -
(rest '(1 2 3))))
(my-map -
(rest '(1 2 3))))
(my-map -
'(2 3)))
(if (empty? '(2 3)) ...))
(cons (- (first '(2 3)))
(my-map -
(rest '(2 3)))))
(cons (- 2)
(my-map -
(rest '(2 3)))))
(cons -2
(my-map -
(rest '(2 3)))))
(cons -2
(my-map - '(3))))
(cons -2
(if (empty? '(3)) ...)))
Running it - abbreviated substitution model
(cons -2
(cons (- (first '(3)))
(my-map
-
(rest '(3))))))
(cons -2
(cons (- 3)
(my-map -
(rest '(3))))))
(cons -2
(cons -3
(my-map -
'()))))
(cons -2
(cons -3
(if (empty? '())
'()
...))))
(cons -2
(cons -3 '())))
(cons -2
'(-3)))
'(-2 -3))
Wait, isn’t all that copying of lists inefficient?
No: cons doesn’t copy anything
Idea: linking instead of copying
Lists and web pages
Page 1
Page 2
Page 3
Page 4
Link to article
Lists and web pages
Page 1
Page 2
Page 3
Page 4
Cons’ed
page
Link to old article
Link to extended article
Growing a list without copying
> (cons 1 '(2 3))
'(1 2 3)
first
rest
1
'(2 3)
But wait…
'(1 2 3 4) is the same as (cons 1 (cons 2 (cons 3 (cons 4 '()))))
Minimalism
Pairs
(define-struct pair� (first rest))
; remember those are attributes
; NOT our fave functions!
first
rest
1
'(2 3)
Pairs
(define-struct pair� (first rest))
; note those are property names
; NOT the functions!
(define (cons first rest)� (make-pair first rest))
(define (first list)� (pair-first list))
(define (rest list)� (pair-rest list))
Racket lists are built out of pairs
> (define foo (list 1 2 3))
> (first foo)
1
> (rest foo)
'(2 3)
>
A list
'(1 2 3 4)
first
rest
Is really a pair
1
'(2 3 4)
first
rest
I mean a pair chained with another pair
1
first
rest
2
'(3 4)
first
rest
Make that three pairs
1
first
rest
2
first
rest
3
'(4)
first
rest
well, really four…
1
first
rest
2
first
rest
3
first
rest
4
'()
Building lists out of pairs
This means that when you “pass a list” to a function
(define foo
(cons 1
(cons 2
(cons 3 '()))))
1
2
3
'()
foo
Growing lists using cons
> (cons 0 foo)
'(0 1 2 3)
1
2
3
'()
foo
0
cons returns this
Lists are recursive data structures
A list is always either:
Lists are recursive data structures
(define (func list)
(if (empty? list)
answer-for-empty-list
answer-for-pair))
Takeaways
Advanced Ideas
Cookbook for Building Lists Recursively
(define (func args …)�(if simple-case?� '()� (cons …figure out a new element to add…� (func …args to build the rest of the list…))))
Building a list recursively
(define (func args …)�(if simple-case?� '()� (cons …new element…� (func …args…))))
(define (my-map f list)�(if (empty? list)� '()� (cons (f (first list))� (map f (rest list)))))
What about iterative recursion when building these?
Building a list iteratively
(define (func args … accumulator)�(if simple-case?� accumulator� (func …args for rest of list…� (cons … new element to add…� accumulator))))
An iterative version of map
(define (loop func in out)
(if (empty? in)� out� (loop func
(rest in)� (cons (func (first in))
out)))
(define (map func list)
(loop func list '())))
(define (func args … accumulator)�(if simple-case?� accumulator� (func …args…� (cons …new element…� accumulator))))
Sometimes iterative recursion is a bad idea