CSE 413 Winter 2001

Assignment 2 -- Scheme Programming

Due: Electronic turnin due no later than 10:00 pm, Tuesday, January 23, 2001.  Paper receipt and written problems due at the beginning of lecture on Wednesday, January 24.

All of these problems should be done without using side effects (i.e. redefining variables or using set!).  Be sure to test your functions on various cases, including empty lists, simple lists with no sublists, complex lists, etc.

Part I.  Written (paper) problems

Suppose we enter the following definitions in at the top-level of a Scheme environment:

(define a 3)
(define b 6)
(define c 12)
(define x (lambda (x) (+ x c)))
(define y (lambda (z) (* 2 (x z))))

What are the results when you evaluate the following expressions

  1. (let ((b 2)
          (a 5))
         (+ a b c))

      
  2. (let ((c 4)
          (b (* a c)))
         (+ b c))
      
  3. (let* ((c 4)
           (b (* a c)))
          (+ b c))
      
  4. (x 5)
      
  5. (let ((c 2))
         (x 5))

      
  6. (map y '(2 3 4 1))
      
  7. (map (lambda (z) (+ a b)) '(1 2 3))

Part II. Data Structures in Scheme

Scheme does not include classes or modules to directly support encapsulation or abstract data types, but there are common idioms for this using functions to abstract away from the underlying representation of data (usually a list).

In this part of the assignment, create some functions to manipulate binary trees and use them for some typical tree problems.  The basic idea is to represent each node in a binary tree using a 3-element list:

        (contents left-child right-child)

The first element, contents, is the value stored in the node.  It can be any scheme object at all  The two other list elements are pointers to the left and right subtrees of this node, if any.  If a subtree is empty, this is indicated by using an empty list () as the value for left-child and/or right-child, as appropriate.

Write and test Scheme functions to solve the following problems.  You should save all of your function definitions for this part of the assignment in a single source file. 

General observation (hint): Binary trees have an elegant, recursive structure.  Take advantage of this to structure your code.

  1. Write functions to create new binary tree nodes and extract fields from existing ones.  The functions you should create are:

            (make-node value left right) => (value left right)
        (value-of '(value left right)) => value
        (left-child '(value left right)) => left
        (right-child '(value left right)) => right


    These functions can be implemented trivially using things like list, cons, car, and cdr.  The advantage of having these functions is that you can write other code in terms of nodes, values, and children, not car, cddadr, and similar things.  The resulting code is usually much easier to read.
      
  2. Write functions to display the values in a binary tree using preorder, postorder, and inorder traversals:

            (display-preorder aTree)
        (display-inorder aTree)
        (display-postorder aTree)

    Test your functions by creating a few trees using nested make-node function calls and then using these as arguments to the traversal functions.
      
  3. Write a function

            (is-member value aTree)

    that evaluates to #t if value appears in some node of the tree, otherwise it should evaluate to #f.  For this problem, value appears in the tree if it is equal? to the value stored in some node.
     
  4. Write a function map-tree that has two arguments, a function and a tree.  Evaluation of

            (map-tree f aTree)

    should return a completely new tree that has the same shape as aTree, but whose node values are calculated by applying the function f to the corresponding node value in the original tree.

Part III. Symbolic Algebra

Symbolic algebra was one of the original application areas for Lisp, the ancestor of Scheme. In this part of the assignment, write a function that solves symbolic equations in one variable. Function solve should have two arguments: a variable var and an equality eqn. The result of (solve var eqn) should be another equality where var appears alone on the left and whose right argument is the solution of the original equation in terms of that variable.

The top level of the input equation eqn should be a list of the form (= exp1 exp2). The two expressions exp1 and exp2 should be legal Scheme expressions formed from atoms, numbers, and function calls involving +, -, *, and / (only). You should assume that each of these functions is binary - has exactly two arguments. You don't need to worry about division by 0.

If there is exactly one occurrence of var in eqn, return the solution. Otherwise, return the atom TOO-HARD. Except for checking for the right number of occurrences of the unknown, you can assume the input is well-formed.

Test your solve function on a number of examples to demonstrate that it is working correctly, including the following. (The answers are given on the next line in each case.)

(solve 'x '(= 3 x))
(= x 3)

(solve 'x '(= (+ x 3) (* 8 y)))
(= x (- (* 8 y) 3))

(solve 'y '(= (+ (+ (* a x) (* b y)) k) 0))
(= y (/ (- (- 0 k) (* a x)) b))

(solve 'x '(= y (/ 1 x)))
(= x (/ 1 y))

(solve 'x '(= (+ (+ (* a (* x x)) (* b x)) c) 0))
TOO-HARD

(solve 'y '(= y (* 2 y)))
TOO-HARD

(solve 'x '(= (* 2 (- 3 x)) (+ y z)))
(= x (- 3 (/ (+ y z) 2)))

Your solution should be written completely in a functional style -- you may not use functions with side effects, such as set!, set-car!, and so forth. Your code should be well-commented. If appropriate, create "data structures" using functions, like the ones we created for the binary tree problems in Part II.  (For example, your code could be much easier to read if you define functions to extract the operator, left operand, and right operand from a binary expression or equation.)

Your Scheme code for this part of the assignment in a separate file from the tree functions created in part II.

What to Hand In

For this (as well as most) assignments, you should turn in your work both electronically over the web and on paper.

Electronic Submission

Turn in a copy of both of your Scheme source files from parts II and III using this turnin form.

Paper Submission

Hand in the following:

  1. Written answers to the problems from Part I.
  2. A printed copy of the receipt you received when you electronically turn in the file containing your Scheme functions from Parts II and III.  This listing will include a copy of your Scheme code.
  3. A printout showing the transcript of the Scheme session demonstrating the results produced by your functions for suitable test cases.  This should contain enough to show that your functions work, but no more than necessary to do that. Part of your grade will be determined by the quality of the test cases you use for this.  (Don't worry if there are some minor typos in the transcript - just draw a line through anything we should ignore.)