Assignment 6: Exploring recursion
- Due
- by 10:30pm on Tuesday, 13 March 2018.
- Summary
- For this assignment, you will apply your knowledge of recursion to solve a variety of problems that require different kinds of general repetition.
- Collaboration
- You must work with your assigned partner(s) on this assignment. You may discuss this assignment with anyone, provided you credit such discussions when you submit the assignment.
- Submitting
- Email your answers to csc151-01-grader@grinnell.edu. The subject of your email should be [CSC151 01] Assignment 6 and should contain your answers to all parts of the assignment. Scheme code should be in the body of the message, not in an attachment.
- Warning
- So that this assignment is a learning experience for everyone, we may spend class time publicly critiquing your work.
Problem 1: Tracing two types of tally
Topics: code reading, recursion
One way to really understand what is happening in a recursive procedure is
to trace the steps as Scheme evaluates the portions of the procedure in its
traditional “inside-out” form. For example, given the non-helper version of
sum, we might write the following trace.
(sum (list 38 12 83))
; Not null
=> (+ (car (list 38 12 83) (sum (cdr (list 38 12 83)))
; Simplify car/cdr
=> (+ 38 (sum (list 12 83)))
; Not null
=> (+ 38 (+ (car (list 12 83)) (sum (cdr (list 12 83)))))
; Simplify car/cdr
=> (+ 38 (+ 12 (sum (list 83))))
; Not null
=> (+ 38 (+ 12 (+ (car (list 83)) (sum (cdr (list 83)))))))
; Simplify car/cdr
=> (+ 38 (+ 12 (+ 83 (sum null))))
; Null case
=> (+ 38 (+ 12 (+ 83 0)))
; Evaluate innermost expression
=> (+ 38 (+ 12 83))
; Evaluate innermost expression
=> (+ 38 95)
; Evaluate innermost expression
=> 133
Consider the two following versions of tally-odd.
;;; Procedure:
;;; tally-odd
;;; Parameters:
;;; nums, a list of integers
;;; Purpose:
;;; Count how many values in nums are odd
;;; Produces:
;;; number-odd, a count of how many values are odd.
(define tally-odd-a
(lambda (nums)
(cond
[(null? nums)
0]
[(not (integer? (car nums)))
(error "tally-odd expects a list of integers. Your list contains "
(car nums))]
[(odd? (car nums))
(+ 1 (tally-odd-a (cdr nums)))]
[else
(tally-odd-a (cdr nums))])))
(define tally-odd-b
(lambda (nums)
(tally-odd-b-kernel nums 0)))
(define tally-odd-b-kernel
(lambda (remaining tally-so-far)
(cond
[(null? remaining)
tally-so-far]
[(not (integer? (car remaining)))
(error "tally-odd expects a list of integers. Your list contains "
(car remaining))]
[(odd? (car remaining))
(tally-odd-b-kernel (cdr remaining) (+ 1 tally-so-far))]
[else
(tally-odd-b-kernel (cdr remaining) tally-so-far)])))
Write traces of the evaluation of tally-odd-a and tally-odd-b (and
tally-odd-b-kernel, since that’s where the real work happens) on the
input '(5 2 7 3 6).
(tally-odd-a '(5 2 7 3 6))
; Not empty, car is odd
=> (+ 1 (tally-odd-a (cdr '(5 2 7 3 6))))
...
(tally-odd-b '(5 2 7 3 6))
=> (kernel '(5 2 7 3 6) 0)
; Not empty, car is odd
=> (kernel (cdr '(5 2 7 3 6)) (+ 1 0))
...
Problem 2: List containment
Topics: recursion, conditionals
Document and write a recursive procedure, (contains? lst val),
that returns true (#t) if val appears in lst and false otherwise.
> (contains? (list 'a 'b 'c) 'a)
#t
> (contains? (list 'a 'b 'c) 'b)
#t
> (contains? (list 'a 'b 'c) 'c)
#t
> (contains? (list 'a 'b 'c) 'd)
#f
Problem 3: Finding the index of an element
Topics: list recursion
As you know, the (index-of val lst) procedure finds the
index of the first instances of val in lst. If val is not in lst,
it returns the value -1.
Suppose index-of did not exist. We could write it ourselves.
Document and write a recursive procedure, (my-index-of val
lst), that provides the first index of val within
lst. If the value does not appear, my-index-of should return #f.
> (my-index-of 'skip (list 'hop 'skip 'and 'jump))
1
> (index-of 5 (list 5 4 3 2 1 2 3 4 5))
0
> (my-index-of "eraser" (list "pencils" "paper" "index cards" "markers" "ball-point pens"))
#f
Note: You may not use list-ref (or index-of) in implementing my-index-of.
Problem 4: Finding the indices of an element
Topics: list recursion
As you know, the (index-of val lst) procedure finds the
index of the first instances of val in lst. If val is not in lst,
it returns the value -1.
What if we wanted all of the indices, not just the first one? We’d probably have to write our own procedure.
Document and write a recursive procedure, (indices-of val
lst), that creates a list of all the position in lst
where val appears. If the value does not appear, indices-of should
return the empty list.
> (indices-of 'skip (list 'hop 'skip 'and 'jump))
'(1)
> (indices-of 'skip (list 'hop 'skip 'jump 'and 'skip 'again))
'(1 4)
> (indices-of 5 (list 5 4 3 2 1 2 3 4 5))
'(0 8)
> (indices-of "eraser" (list "pencils" "paper" "index cards" "markers" "ball-point pens"))
'()
Note: You may not use list-ref (or index-of) in implementing indices-of.
Problem 5: Riffling lists
Topics: list recursion
Document and write a procedure, (riffle2 first second), that
produces a new list containing alternating elements from the lists
first and second. If one list runs out before the other, then the
remaining elements should appear at the end of the new list.
> (riffle2 (list 'a 'b 'c) (list 'x 'y 'z))
'(a x b y c z)
> (riffle2 (list 'a 'b 'c) (iota 10))
'(a 0 b 1 c 2 3 4 5 6 7 8 9)
> (riffle2 (iota 10) (list 'a 'b 'c)
'(0 a 1 b 2 c 3 4 5 6 7 8 9)
> (riffle2 null (list 'a 'b 'c))
'(a b c)
Problem 6: Riffling lists, revisited
Topics: list recursion
Document and write a procedure, (riffle3 first second third),
that produces a new list containing alternating elements from the lists
first, second, and third. If one list runs out before the others,
continue riffling the remaining two lists. (You may choose to use riffle2
when you reach that point.)
> (riffle3 (list 'a 'b 'c) (list 'p 'q 'r) (list 'x 'y 'z))
'(a p x b q y c r z)
> (riffle3 (list 'a 'b 'c 'd) (list 'e 'f) (list 'g 'h 'i))
'(a e g b f h c i d)
> (riffle3 (list 'a 'b 'c) null (list 'd 'e 'f))
'(a d b e c f)
> (riffle3 (list 'c 'b 'a) (list 'd 'd 'd) (list 'e 'f 'g))
'(c d e b d f a d g)
Problem 7: Generating permutations
Topics: recursion
Given a list of items, it can be useful to know all possible ways the list can be uniquely ordered. A specific ordering of items is called a permutation and the notion appears frequently in mathematics.
Write, but do not document, a procedure called (permutations
lst) that takes a list as a parameter returns a list of all
possible permutations of the elements of lst. Here are a few example
executions of the procedure.
> (permutations (list 1 2))
'((1 2) (2 1))
> (permutations (list 0 1 2))
'((0 1 2) (1 0 2) (1 2 0) (0 2 1) (2 0 1) (2 1 0))
> (permutations (list 'a 'b 'c))
'((a b c) (a c b) (b a c) (b c a) (c a b) (c b a))
> (permutations (list "a" 'b 7))
'(("a" b 7) ("a" 7 b) (b "a" 7) (b 7 "a") (7 "a" b) (7 b "a"))
> (permutations (list "hello"))
'(("hello"))
> (permutations null)
'(())
You will find the following procedure helpful. (You should understand what the procedure does. You need not understand how it achieves its goal.)
;;; Procedure:
;;; insert-everywhere
;;; Parameters:
;;; val, a Scheme value
;;; lst, a list
;;; Purpose:
;;; Create a new list consisting of the result of inserting `val`
;;; at every possible location in `lst`.
;;; Produces:
;;; inserted, a list of lists
;;; Preconditions:
;;; [No additional]
;;; Postconditions:
;;; * (length inserted) = (+ 1 (length lst))
;;; * (list-ref inserted i) = is the result of inserting val before
;;; the value at position i in lst.
;;; Practica:
;;; > (insert-everywhere 'a (list 'b))
;;; '((a b) (b a))
;;; > (insert-everywhere 'a (list 1 2 3))
;;; '((a 1 2 3) (1 a 2 3) (1 2 a 3) (1 2 3 a))
;;; > (insert-everywhere 'a null)
;;; '((a))
(define insert-everywhere
(lambda (val lst)
(if (null? lst)
(list (list val))
(cons (cons val lst)
(map (section cons (car lst) <>)
(insert-everywhere val (cdr lst)))))))
How will this procedure help? To compute the permutations of a non-empty list, you make all of the permutations of the cdr of the list, insert the car of the list at each position in each permutation, and then join them all together.
> (insert-everywhere 1 '(2))
'((1 2) (2 1))
> (insert-everywhere 0 '(1 2))
'((0 1 2) (1 0 2) (1 2 0))
> (insert-everywhere 0 '(2 1))
'((0 2 1) (2 0 1) (2 1 0))
> (append (insert-everywhere 0 '(1 2)) (insert-everywhere 0 '(2 1)))
'((0 1 2) (1 0 2) (1 2 0) (0 2 1) (2 0 1) (2 1 0))
Problem 8: Joining sorted lists
Topics: list recursion, sorting
Write but do not document a recursive procedure, (merge-sorted-lists
lst1 lst2), that takes as input two lists of real numbers
that are ordered from smallest to largest and returns the combination
of those two lists, still in sorted order.
You may not use sort in your solution.
> (merge-sorted-lists '(1 5 6 7 22) '(0 2 3 4 7 18 19))
'(0 1 2 3 4 5 6 7 7 18 19 22)
> (merge-sorted-lists '(1 3 5 7) '(2 3 4 5 6))
'(1 2 3 3 4 5 5 6 7)
Evaluation
We will primarily evaluate your work on correctness (does your code compute what it’s supposed to and are your procedure descriptions accurate); clarity (is it easy to tell what your code does and how it achieves its results; is your writing clear and free of jargon); and concision (have you kept your work short and clean, rather than long and rambly).