Skip to main content

Assignment 7: Exploring recursion

Assigned
Wednesday, 3 April 2019
Due
Tuesday, 9 April 2019 by 10:30pm
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 7 and should contain your answers to all parts of the assignment. Please send your scheme code in an attached .rkt file.

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 lst val) procedure finds the index of the first instance of val in lst. If val is not in lst, it returns the value #f.

Suppose index-of did not exist. We could write it ourselves.

Document and write a recursive procedure, (my-index-of lst val), that provides the first index of val within lst. If the value does not appear, my-index-of should return #f.

> (my-index-of (list 'hop 'skip 'and 'jump) 'skip)
1
> (my-index-of (list 5 4 3 2 1 2 3 4 5) 5)
0
> (my-index-of (list "pencils" "paper" "index cards" "markers" "ball-point pens") "eraser")
#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 lst val) procedure finds the index of the first instance of val in lst. If val is not in lst, it returns the value #f.

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 two 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)

Note: There’s a clever approach that involves looking only at the first list (but changing which list is first). You need not undertake this approach, but you should spend a minute or two thinking about it.

Problem 6: Riffling three lists

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.

> (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)

Note: You are permitted to use riffle2 when you run out of elements in one of the lists.

Problem 7: Joining sorted lists of words

Topics: list recursion, sorting

Write but do not document a recursive procedure, (merge-sorted-lists lst1 lst2), that takes as input two lists of strings that are each ordered alphabetically and returns the combination of those two lists, still in alphabetical order

You may not use sort in your solution.

> (merge-sorted-lists '("alpha" "gamma" "yak" "zebra")
                      '("bracket" "cheese" "hello" "world" "zap"))
'("alpha" "bracket" "cheese" "gamma" "hello" "world" "zap" "zebra")
> (merge-sorted-lists '("alpha" "delta" "echo" "foxtrot" "xerxes")
                      '("alpha" "delta" "gamma" "xerxes"))
'("alpha" "alpha" "delta" "delta" "echo" "foxtrot" "gamma" "xerxes" "xerxes")

Note: The input lists must be in alphabetical order. You need not handle inputs not in alphabetical order.

Problem 8: Checking for alphabetical order

Topics: lists, recursion, sorting

Write a procedure, (alphabetical-order? words), that takes a list of strings as input and returns true if the words are in alphabetical order (aka ASCII order) and false otherwise. You may assume that all of the elements in the list are strings.

> (alphabetical-order? '("alpha" "beta" "delta" "epsilon"))
#t
> (alphabetical-order? '("alpha" "beta" "gamma" "delta" "epsilon"))
#f
> (alphabetical-order? '())
#t

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).