Diferenciação Analítica
Um outra maneira de utilizar Scheme como metalinguagem circular é na obtenção da derivada analítica de uma função. Nesse caso, não é o procedimento que computa a função que é passado ao gerador (como era o caso na diferenciação numérica utilizada no método de Newton de uma seção anterior), mas sim uma descrição de como seria o código desse procedimento na linguagem Scheme.
(define (differentiate var sexpr)
(cond ((number? sexpr) 0)
((variable? sexpr)
(if (same-variable? sexpr var) 1 0))
((sum? sexpr)
(make-sum (differentiate var (augend sexpr))
(differentiate var (addend sexpr))))
((product? sexpr)
(make-sum
(make-product (multiplier sexpr)
(differentiate var (multiplicand sexpr)))
(make-product (differentiate var (multiplier sexpr))
(multiplicand sexpr))))
(else
(error "unknown expression type -- DERIV" sexpr))))
(define (make-derivative var sexpr)
(let ((deriv (differentiate var sexpr)))
(eval
`(lambda (,var) ,deriv)
(interaction-environment))))
(define (variable? x)
(symbol? x))
(define (same-variable? v1 v2)
(and (variable? v1) (variable? v2) (eq? v1 v2)))
(define (make-sum augend addend)
(cond ((and (number? addend) (number? augend)) (+ augend addend))
((and (number? augend) (= augend 0)) addend)
((and (number? addend) (= addend 0)) augend)
(else (list '+ augend addend))))
(define (sum? x)
(and (list? x) (eq? (car x) '+)))
(define (augend s)
(cadr s))
(define (addend s)
(caddr s))
(define (make-product multiplier multiplicand)
(define (=number? sexpr num)
(and (number? sexpr) (= sexpr num)))
(cond ((or (=number? multiplier 0) (=number? multiplicand 0))
0)
((and (number? multiplier) (number? multiplicand))
(* multiplier multiplicand))
((=number? multiplier 1) multiplicand)
((=number? multiplicand 1) multiplier)
(else (list '* multiplier multiplicand))))
(define (product? x)
(and (list? x) (eq? (car x) '*)))
(define (multiplier p)
(cadr p))
(define (multiplicand p)
(caddr p))
Adicione mais algumas regras de diferenciação no programa acima.
A partir do código dado temos um sistema capaz de gerar procedimentos que computam polinômios arbitrários (de uma variável independente), assim como suas derivadas (incluindo derivadas parciais).
(define coefs '(-1 -1 1))
(define p
(apply horner
(list 'x coefs))) ;; p = x^2 - x - 1
(define f
(apply make-polynomial
(cons 'x coefs))) ;; f(x) = p(x)
(define df
(make-derivative 'x p)) ;; df(x) = p'(x) = 2x - 1
Pense sobre como seria possível expandir esse programa para permitir a geração de polinômios multivariados. Você pode discutir suas ideias na seção de comentários desse site.