CSc 404 Assignment 2

Poly Want A Derivative?

Assigned
Due

Feb 3
60 pts
Feb 18
This assignment involves making an improvement to the derivative example. The existing code does not use one of the most common and simple rules for derivatives: the Power Rule that dAxn=nAxn1 for variable x, constant A and positive integer n. (The rule is actually more general, but that's what we're using.) In this project, you will complete some given code to add this feature. Don't panic; you're only writing two functions. Here's what you need to do.
  1. Download these two files and store them in the same directory:
  2. The a2skel.lsp code loads diff.lsp, and allows you to run the d and simplify methods from diff.lsp. Until you make changes, these will operate the same as original diff code. Make sure it does work:
    [bennet@desktop asst]$ tomslsp Welcome to Tom's Lisp 0.97. Recur well. lsp>(load "a2skel.lsp") diff-Axn lsp>(simplify (d 'x '(+ (* 3 x x) (* 2 x) 10)))) (+ (* 3 (+ x x)) 2) lsp>(simplify (d 'x '(+ (* 5 x x x ) (* -7 x x ) (* 3 x) 17))) (+ (* 5 (+ (* x (+ x x)) (* x x))) (* -7 (+ x x)) 3) lsp>
  3. Replace the stubs for the functions Axn? and diff-Axn with working versions. (More on this below.)
  4. When those changes are successful, the derivative function will do a much cleaner job on expressions which contain power terms:
    [bennet@desktop diff]$ tomslsp Welcome to Tom's Lisp 0.97. Recur well. lsp>(load "a2complete.lsp") diff-Axn lsp>(simplify (d 'x '(+ (* 3 x x) (* 2 x) 10)))) (+ (* 6 x) 2) lsp>(simplify (d 'x '(+ (* 5 x x x ) (* -7 x x ) (* 3 x) 17))) (+ (* 15 x x) (* -14 x) 3) lsp>

The Calculus

If you have managed to push this bit of calculus out of your head, what you need for this assignment is quite simple. As noted above, the power rule is dAxn=nAxn1. It applies to any formula of the correct form, such as 5x3. To take its derivative, just multiply the exponent 3 by the coefficient 5 to make a new coefficient, and subtract one from the exponent, so the derivative is 15x2. Or again, d7y4=28x3. It still works when the exponent is one, but we usually write the result in a simplified way. d7x=d7x1=7x0=7.

The Programming

The existing derivative program does not have an exponent notation, so we will write terms like this by repeating x. (Re-writing the code to recognize an exponent operator might make a nice class project for a grad student, if you're looking for one.) So your code will have to convert an expression like (* 5 x x x) to (* 15 x x).

The a2skel.lsp code starts by loading the existing diff code. It then re-defines the d function that takes derivatives with a new version that adds an additional case to the main cond:

(define (d x E) (cond ((constant? E) (diff-constant x E)) ((variable? E) (diff-variable x E)) ((sum? E) (diff-sum x E)) ((Axn? x E) (diff-Axn x E)) ; New case here. ((product? E) (diff-product x E)) (#t (error ERR_BADEXPR "Cannot parse expression.")) ) )
The cond sees what type of expression is has been given, and calls the appropriate derivative function. The new version adds a case for expressions of the form Axn. Both Axn? and diff-Axn are also given in the file as stubs which you must replace with correct versions. Presently, Axn? always returns false, which keeps diff-Axn from ever being called. If it is called, it just throws.

A correct Axn? takes two parameters: a variable name and an expression. It returns #t exactly when the expression is a product of zero or more constants and one or more of the indicated variable. You need to allow for any number of constants in any position among the factors. You also need to make sure there is at least one of the specified variable. Return false if there is any other sort of factor.

The function diff-Axn applies the power rule. It also takes a variable and an expression. For instance, it will convert (* 4 x x) to (* 8 x). It must also be able to deal with situations where the constant is distributed, or missing, so (* x 3 x x 4) will become (* 36 x x), and (* x x) becomes (* 2 x). You may assume that the expression is of the correct form, since it will have been approved by Axn?. (In fact, you'll have little use for the variable parameter, since Axn? approval means the expression is a function of x.)

Some examples:

lsp>(Axn? 'x '(* 3 x x x)) #t lsp>(Axn? 'y '(* y y)) #t lsp>(Axn? 'x '(* y y)) nil lsp>(Axn? 'q '(* 5 q 6 q 7 q)) #t lsp>(Axn? 'u '(+ 5 u u u u)) nil lsp>(Axn? 'w '(w w w)) nil lsp>(Axn? 'x '(* x 10 (* x x))) nil lsp>(Axn? 'x '(+ x)) nil lsp>(Axn? 'x '(* 4 9)) nil lsp>(diff-Axn 'x '(* 4 x x)) (* 8 x) lsp>(diff-Axn 'x '(* x 3 x x 4)) (* 36 x x) lsp>(diff-Axn 'y '(* y y y y)) (* 4 y y y) lsp>(diff-Axn 'z '(* z)) (* 1) lsp>(diff-Axn 'q '(* 5 9 q 3)) (* 135) lsp>(d 'x '(+ (* x 5 x x) (* x x) (* 10 x) 12)) (+ (* 15 x x) (* 2 x) (* 10) 0) lsp>(simplify (d 'x '(+ (* x 5 x x) (* x x) (* 10 x) 12))) (+ (* 15 x x) (* 2 x) 10) lsp>(d 'y '(+ (* 13 y y y y) (* y 3 y 4 y) (* 8 y) )) (+ (* 52 y y y) (* 36 y y) (* 8)) lsp>(d 'g '(* (+ (* 2 g) (* 7 g g g)) (+ g g 8))) (+ (* (+ (* 2 g) (* 7 g g g)) (+ 1 1 0)) (* (* (+ g g 8)) (+ (* 2) (* 21 g g)))) lsp>(simplify (d 'g '(* (+ (* 2 g) (* 7 g g g)) (+ g g 8)))) (+ (* (+ (* 2 g) (* 7 g g g)) (+ 1 1)) (* (+ g g 8) (+ 2 (* 21 g g))))

You can, and should, unit-test your functions just as shown above. Lisp makes this pretty easy to do.

You may add any helper functions you like. The skeleton contains two additional functions, collect-constants and all-the-same?. As far as I can tell, they do what their comments say they do. Feel free to run them yourself to help understand them. They are not presently called anywhere, but are provided for you to use as you like. Call either or both if you find them useful, steal ideas from them, or ignore them entirely.

Submitting Your Work

When your function works, is nicely formatted and documents, submit it using this form. Please send the original skeleton (use any name you like) with your additions and changes.