CS 307: 13. Symbolic Algebra in Java

Due: Friday, December 7, 2001.

This assignment illustrates tree recursion and the use of run-time polymorphism of the kind with which we are familiar in Scheme.

The directory (folder) exp contains files L.java, Cons.java, Symbol.java, and Number.java. (Note that this file Cons.java is different from the one used in the previous assignment; be sure to keep it in a different folder so that you do not over-write your previous assignment.)   These files implement a small subset of Scheme types as Java classes. L is the top class, representing any Lisp object; Cons, Symbol, and Number are subclasses of L.

Study the classes that are provided so that you can see how the Scheme functions and concepts that you are familiar with are implemented.

Some useful functions are provided in the file Cons.java, both as examples of how to write code in this system and as functions that may be useful in your programs.

This assignment will re-implement some of the functions that were written for our Assignment 8 on Symbolic Algebra. It may be helpful for you to consult your solution to Assignment 8 while writing these programs. The class notes are also legitimate sources for some of these functions.

Implement the following methods in Java; add them to the file Cons.java, which also has test cases for these in its main() method.

  1. assoc(item, lst) Use L.eqv to test for item.
  2. union(lst, second)
  3. copy_tree(tree)
  4. subst(x, y, tree) substitutes x for y in tree structure tree. y is a simple item that can be tested with L.eqv.
  5. sublis(alist, tree)   For this version of sublis, we will assume that the alist argument contains lists of two items, ((var value) ... ), rather than "dotted pair" items.
  6. vars(exp) returns a list of the variables in a well-formed formula or expression exp.
       (vars 'a)        =>  (a)        (vars '(+ a a))  =>  (a)
       (vars '(= a b))  =>  (a b)      (vars '(+ a 2))  =>  (a)
    
  7. Write an interpreter function evalexp(exp) that evaluates an expression exp containing only constant numeric values and the operators + - * /; - can be either unary or binary.
       evalexp( (+ 3 (* 4 5)) )  =>  23
    
  8. Write a recursive function solve(exp, var) that attempts to solve the equation exp for variable var, which we assume occurs at most once in exp. The basic idea of solve is to test for equations that are already solved or unsolvable (base cases); otherwise, solve will search for a solution by applying laws of algebra. solve is a kind of tree recursion -- not recursion on car and cdr, but recursion on the applicable operators (transformations of the input formula into new formulas).
    1. If the left-hand side (lhs) of exp is var, return exp.
         (solve '(= f (* m a)) 'f)  =>  (= f (* m a))
      
    2. If the right-hand side (rhs) of exp is var, return exp with its arguments reversed.
         (solve '(= (* m a) f) 'f)  =  (= f (* m a))
      
    3. If the rhs is not var and not a list, return null.
    4. If the rhs of exp is a list (i.e. an operation), try to solve the equation by applying algebraic laws, which will make a new equation by moving things to the left side. Then try to solve that equation. For binary operators, there will be two possibilities, both of which must be tried. For example, given the original equation x = y - z, we could (a) add z to both sides to give x + z = y, or (b) subtract y from both sides to give x - y = - z and then negate both sides to give y - x = z (these two operations can be combined as a single transformation).
         (solve '(= x (- y z)) 'y)
            (solve '(= (- y x) z) 'y)   ; first try:  null
            (solve '(= (+ x z) y) 'y)   ; second try: succeeds
          =>  (= y (+ x z))
      
    You should handle the operators + - * /. The operator - can be either unary (having only one argument, i.e. minus) or binary (having two arguments, i.e. difference), which must be treated differently. We will assume that all other operators will have two arguments.

    Demonstrate that you can solve the test cases provided in the file Cons.java .

  9. Write a function infix(exp, parens) that will convert an expression into a string in infix form. Include parentheses around an expression only if it involves an operation and parens is true. For recursive calls to infix, you can always make parens be true.