See https://courses.cs.washington.edu/courses/cse341/15wi/racket/deriv.rkt
A classic example of Scheme/Racket programs as data: symbolic differentiation. Adapted from "Structure and Interpretation of Computer Programs" Chapter 2. Somewhat modified: doesn't use constructor functions, and it separates out finding the derivative from simplifying expressions.
Includes unit tests.
The top-level function deriv takes an expression and a variable, and returns the derivative of that expression with respect to the variable, in simplified form
(require racket)
(require racket/base)
(define (deriv exp var)
(simplify (basic-deriv exp var)))
basic-deriv
takes the derivative of an expression exp with respect to a variable and returns the result without simplification
(define (basic-deriv exp var)
(cond ((number? exp) 0)
((symbol? exp)
(if (eq? exp var) 1 0))
((sum? exp)
(list '+ (basic-deriv (left exp) var) (basic-deriv (right exp) var)))
((product? exp)
(list '+
(list '* (left exp) (basic-deriv (right exp) var))
(list '* (basic-deriv (left exp) var) (right exp))))
(else (error "unknown expression type -- basic-deriv" exp))))
Test whether a list structure is a sum
(define (sum? x)
(and (pair? x) (eq? (car x) '+)))
Test whether a list structure is a product.
(define (product? x)
(and (pair? x) (eq? (car x) '*)))
Get the left hand part of a sum or product
(define (left exp)
(cadr exp))
Get the right hand part of a sum or product
(define (right exp)
(caddr exp))
Basic simplification function (nothing too fancy ... doesn't know about commutativity or associativity)
(define (simplify exp)
(cond ((sum? exp) (simplify-sum exp))
((product? exp) (simplify-product exp))
;; if we get here, we can't simplify exp
(else exp)))
(define (simplify-sum exp)
;; to simplify a sum, we need to first recursively simplify the left and right parts
(let ((a (simplify (left exp)))
(b (simplify (right exp))))
(cond ((equal? 0 a) b)
((equal? 0 b) a)
((and (number? a) (number? b)) (+ a b))
(else (list '+ a b)))))
(define (simplify-product exp)
(let ((a (simplify (left exp)))
(b (simplify (right exp))))
(cond ((or (equal? 0 a) (equal? 0 b)) 0)
((equal? 1 a) b)
((equal? 1 b) a)
((and (number? a) (number? b)) (* a b))
(else (list '* a b)))))
See http://docs.racket-lang.org/rackunit/quick-start.html for documentation on Racket's unit testing library.
(require rackunit)
(define deriv-tests
(test-suite
"tests for deriv program"
(check-equal? (deriv 'x 'x) 1 "deriv of x wrt x")
(check-equal? (deriv 'y 'x) 0 "deriv of y wrt x")
(check-equal? (deriv '(+ x 3) 'x) 1 "deriv of (+ x 3) wrt x")
(check-equal? (deriv '(* (+ 2 3) x) 'x) 5 "deriv of unsimplified expression")
(check-equal? (deriv '(+ x y) 'x) 1 "deriv of (+ x y) wrt x")
;; simplification is not as clever as it could be in the following case:
(check-equal? (deriv '(* (+ x 1) (+ x -1)) 'x) '(+ (+ x 1) (+ x -1)) "deriv of (* (+ x 1) (+ x -1)) wrt x")
(check-equal? (deriv '(* (* x y) (+ x 3)) 'x) '(+ (* x y) (* y (+ x 3))) "complex deriv")
))
(require rackunit/text-ui)
;; this line runs the tests ....
(run-tests deriv-tests)
|
(deriv 'x 'x)
(deriv 'y 'x)
(deriv '(+ x 3) 'x)
(deriv '(* x (+ 2 3)) 'x)
(deriv '(+ x y) 'x)
(basic-deriv '(+ x y) 'x)
(deriv '(* (+ x 1) (+ x -1)) 'x)
(deriv '(* (* x y) (+ x 3)) 'x)
(require "calculus.rkt")
(deriv '(* (* x y) (+ x 3)) 'x)
(deriv '(sin x) 'x)
(deriv '(* x 2 x x 3) 'x)
(deriv '(+ (* 2 (** x 5)) (* 4 (** x 3))) 'x)
(deriv '(* (+ x 1) (+ x -1)) 'x)