Poly Want A Derivative?
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=nAxn−1 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.
- Download these two files and store them in the same directory:
- 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>
- Replace the stubs for the functions Axn? and diff-Axn
with working versions.
(More on this below.)
- 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=nAxn−1. 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.