University of Virginia, Department of Computer Science
CS200: Computer Science, Spring 2002

Exam 1 - Answers Out: 25 February 2002
Due: 27 February 2002, 11:00AM

Each question is worth 10 points. You get 30 points for showing the good judgement and wisdom to take this class, to make the total out of 100.

QuestionAverage8 or higher
1. Describe the language defined by this recursive transition network using Backus-Naur Form:

A digit is 0, 1, 2, ..., 9. The symbols inside circles (., +, and -) are terminals. Your Backus-Naur Form grammar should not use the * shortcut notation. For full credit, your grammar should be the shortest possible grammar that describes the same language.

constant ::= sign unsigned-number

sign ::= +
sign ::= -
sign ::=

unsigned-number ::= digits unsigned-number ::= digits . digits

digits ::= digit
digits ::= digits digit

There must be at least one digit. The most common mistake on this problem was to allow unsigned-numbers with no digits, which is not possible with the RTN shown.

This RTN was based on the Pascal User Manual and Report by Kathleen Jensen and Niklaus Wirth (1975). It describes the Pascal language using both RTNs and BNF.

2. Ben Bitdiddle suggests implementing length, a procedure that takes a list as a parameter and evaluates to the number of elements in the list, by first replacing every element in the list with 1, and then summing the list. You may assume you have a procedure sumlist that takes a list of numbers and evaluates to the sum of all the numbers in the list.

For example, we can determine the length of the list (list 1 2 3 "alpha" "beta") by constructing the list (list 1 1 1 1 1) and evaluating (sumlist (list 1 1 1 1 1)) to get 5.

Ben started to define his length procedure below, but couldn't figure out what to put in the space where the box is. Finish the definition of length by providing a Scheme expression for the missing parameter to map:

   (define (bens-length lst)
         (map _________________________________________ lst)))

We want a procedure that takes one parameter and evaluates to 1:

(lambda (p) 1)

3. Louis Reasoner suggests that a general procedure to make a constant function would be useful for defining functions like bens-length. That is, he would like a procedure that takes one parameter, and evaluates to a procedure that produces that value when applied to anything. For example,
> ((make-constant-function "apple") "orange")
> (((make-constant-function (make-constant-function 12)) "orange") 35)
Define make-constant-function.

(define (make-constant-function cst) (lambda (p) cst))
Note that (make-constant-function 1) evaluates to:
((lambda (cst) (lambda (p) cst)) 1) (Using Evaluation Name rule for make-constant-function)
(lambda (p) 1) (Using compound application rule, binding 1 to cst)
so we could answer question 2 now as (make-constant-function 1).
4. The bens-length procedure (and the Scheme primitive length) counts only the elements in the top-level list. For example, (bens-length (list (list 1 2 3) 4)) ==> 2 since the inner list (list 1 2 3) only counts as one element. Alyssa suggests defining a length-deep procedure that evaluates to the total number of elements in a list and all its sub-lists. For example, (length-deep (list (list 1 2 3) 4)) ==> 4 and (length-deep (list (list (list 1 2) (list 3 4) (list 5 (list 6 7)))) ==> 7. Alyssa claims she can define length-deep using only these Scheme primitives: null?, list?, +, 0, 1, car and cdr. Define length-deep, a procedure with one list parameter that evaluates to the total number of elements in the list and all its sub-lists.

Answer: This one was pretty tricky. The easiest is to define length-deep recursively. Note that we need to call length-deep for both the car and cdr of the list, since the car might be a list. Here's how:
(define (length-deep lst)
    (if (null? lst) 0
        (+ (if (list? (car lst)) (length-deep (car lst)) 1)
           (length-deep (cdr lst)))))
A different approach was to first flatten the list, and then use sumlist:
(define (flattenlist slst)
  (if (null? slst) slst
      (if (list? (car slst))
	  (append (flattenlist (car slst))
		  (flattenlist (cdr slst)))
	  (cons (car slst) (flattenlist (cdr slst))))))

(define (length-deep lst)
  (bens-length (flattenlist lst)))
5. Define a procedure reverse that takes a list parameter and evaluates to a list containing the elements of the original list in reverse order. You may find it useful to define extra procedures also. You may not use the Scheme primitive reverse procedure. For this question, do not use insertlg. Example:
> (reverse (intsto 5))
(5 4 3 2 1)
Answer: We can think of reversing a list recursively: we add the first element of the list to the end of reversing the rest of the list. For example, to reverse (1 2 3 4 5) we put 1 at the end of the result from reversing the list (2 3 4 5).

A common (but not quite correct) answer was:

(define (mreverse lst)
  (if (null? lst) lst
      (cons (mreverse (cdr lst)) (car lst))))
This puts the elements in the reverse order, but does not produce a proper list. For example (reverse (intsto 5)) would evaluate to (((((() . 5) . 4) . 3) . 2) . 1).

One solution would be to use append instead of cons:

(define (reverse lst)
  (if (null? lst) lst
      (append (reverse (cdr lst)) (list (car lst)))))
Another approach would be to turn the result of the first implementation into a proper list. We do this by defining a procedure listify, similar to flattenlist used above:
(define (listify slst)
  (if (null? slst) slst
      (if (pair? slst)
          (append (listify (car slst)) (listify (cdr slst)))
          (list slst))))

(define (reverse lst) (flattencons (mreverse lst)))
6. Define reverse (with the same meaning as the previous question) using insertlg.

(define (reverse lst)
  (insertlg (lambda (x rest)
	      (append rest (list x)))
	    lst null))
7. Orders of Growth
  1. How much work is your reverse procedure from Question 5.
  2. How much work is your reverse procedure from Question 6.
Express your answers using Θ and n for the length of the list. Assume cons, car, cdr, and null? take constant time (that is, the time they take does not depend on the length of the list).
Answer: For the definition using append:
(define (reverse lst)
  (if (null? lst) lst
      (append (reverse (cdr lst)) (list (car lst)))))
We evaluate append once for each sub-list, that is n times total. Each append is Θ(n), assuming the definition from SICP:
(define (append list1 list2)
  (if (null? list1) 
      (cons (car list1) (append (cdr list1) list2))))
This does one cons for each element in the first lists. So, reverse as defined about is Θ(n2).

The definition using insertlg is also Θ(n2). It evaluates append exactly the same way as the previous definition.

There are ways to reverse a list in Θ(n), but its surprisingly tricky to do this in Scheme. If you can do this, you get 10 points added to your exam score. You might want to start by considering the listify version above (which is not Θ(n), but could be turned into something that is). You will know your answer is correct if the number of cons evaluations for reversing (intsto 10) is twice the number for reversing (intsto 5).

CS 655 University of Virginia
Department of Computer Science
CS 200: Computer Science
David Evans
Using these Materials