swflint
10/20/2015 1:42:00 AM
Hey,
I was wondering, would you all be willing to critique the following
code? I feel like it could be cleaner and/or more efficient. Any tips?
Thanks,
Sam
Code follows:
;;;; derive.lisp
;;;;
;;;; Copyright (c) 2015 Samuel W. Flint <swflint@flintfam.org>
(defpackage #:derive
(:use #:cl)
(:export :derive
:csc
:sec
:define-equation-functions
:take-derivative))
(in-package #:derive)
;;; "derive" goes here.
(defvar *rules* '())
(defun get-expansion (expression)
(third (first
(remove-if-not #'(lambda (nte)
(let ((test (second nte)))
(apply test expression)))
*rules*))))
(defun generate-match-expression (on arity &optional (type '=))
(declare (symbol on type)
(integer arity))
(case type
(=
`(and (eq function ',on)
(= arg-count ,arity)))
(>
`(and (eq function ',on)
(> arg-count ,arity)))
(>=
`(and (eq function ',on)
(>= arg-count ,arity)))))
(defmacro def-expansion (name (on arity &optional type) (&rest arguments) &body expansion)
(let ((match-expression (if type
(generate-match-expression on arity type)
(generate-match-expression on arity)))
(test-name (intern (string-upcase (format nil "~a-test" name))))
(expansion-name (intern (string-upcase (format nil "~a-expansion" name)))))
`(progn
(defun ,test-name (function &rest arguments &aux (arg-count (length arguments)))
,match-expression)
(defun ,expansion-name (,@arguments)
,@expansion)
(push (list ',name
#',test-name
#',expansion-name)
*rules*)
',name)))
(defun derive (function)
(declare (cons function))
(let ((op (first function)))
(cond
((numberp op)
0)
((and (symbolp op)
(= 1 (length function)))
1)
(t
(let ((expansion-function (get-expansion function)))
(if (functionp expansion-function)
(apply expansion-function (rest function))
(error "Undefined expansion: ~a" op)))))))
(def-expansion mult/2 (* 2) (first second)
(cond
((numberp first)
`(* ,first ,(derive (if (listp second) second (list second)))))
((numberp second)
`(* ,second ,(derive (if (listp first) first (list second)))))
(t
`(+ (* ,first ,(derive (if (listp second) second (list second))))
(* ,second ,(derive (if (listp first) first (list first))))))))
(def-expansion mult/3+ (* 3 >=) (first &rest rest)
(derive `(* ,first ,(cons '* rest))))
(def-expansion div/2 (/ 2) (numerator denominator)
`(/ (- (* ,numerator ,(derive (if (listp denominator) denominator (list denominator))))
(* ,denominator ,(derive (if (listp numerator) numerator (list numerator)))))
(expt ,denominator 2)))
(def-expansion plus/2+ (+ 2 >=) (&rest clauses)
`(+ ,@(map 'list #'(lambda (clause)
(if (listp clause)
(derive clause)
(derive (list clause))))
clauses)))
(def-expansion minus/2+ (- 2 >=) (&rest clauses)
`(- ,@(map 'list #'(lambda (clause)
(if (listp clause)
(derive clause)
(derive (list clause))))
clauses)))
(def-expansion exp/1 (exp 1) (expression)
(if (listp expression)
`(* (exp ,expression) ,(derive expression))
(if (numberp expression)
0
`(exp ,expression))))
(def-expansion expt/2 (expt 2) (base exponent)
(if (numberp exponent)
(if (listp base)
`(* ,exponent (expt ,base ,(1- exponent)) ,(derive base))
`(* ,exponent (expt ,base ,(1- exponent))))
`(* (expt ,base ,exponent) (log ,base))))
(def-expansion log/1 (log 1) (expression)
`(/ ,(derive (if (listp expression) expression (list expression))) ,expression))
(def-expansion log/2 (log 2) (number base)
(declare (ignorable number base))
`(/ (derive (cons 'log number)) (* (log ,base) ,number)))
(def-expansion sin/1 (sin 1) (arg)
`(* (cos ,arg) ,(derive (if (listp arg) arg (list arg)))))
(def-expansion cos/1 (cos 1) (arg)
`(* (- (sin ,arg)) ,(derive (if (listp arg) arg (list arg)))))
(def-expansion tan/1 (tan 1) (arg)
`(* (expt (sec ,arg) 2) ,(derive (if (listp arg) arg (list arg)))))
(def-expansion csc/1 (csc 1) (arg)
`(* (- (csc ,arg)) (cot ,arg) ,(derive (if (listp arg) arg (list arg)))))
(def-expansion sec/1 (sec 1) (arg)
`(* (sec ,arg) (tan ,arg) ,(derive (if (listp arg) arg (list arg)))))
(def-expansion cot/1 (cot 1) (arg)
`(* (- (expt (csc ,arg) 2)) ,(derive (if (listp arg) arg (list arg)))))
(defun csc (x)
"csc -- (csc x)
Calculate the cosecant of x"
(/ (sin x)))
(defun sec (x)
"sec -- (sec x)
Calculate the secant of x"
(/ (cos x)))
(defmacro define-equation-functions (name variable equation)
(let ((derivative-name
(intern
(string-upcase
(format nil "d/d~a-~a" variable name))))
(derivative (derive equation)))
`(progn
(defun ,name (,variable)
,equation)
(defun ,derivative-name (,variable)
,derivative))))
(defmacro take-derivative (equation)
(let ((derivative (derive equation)))
`',derivative))
;;; End derive