Я пытаюсь создать функцию символического вывода в схеме Chez. Работает нормально (упрощение еще не делается):
(define (derive var expr)
;; var is the direction in which you would like to derive
(if (list? expr)
(case (car expr)
('+ (sum-rule var expr))
('- (sub-rule var expr))
('* (prod-rule var expr))
;; other rules
(else (atomic-rule var expr )))
(atomic-rule var expr)))
(define (atomic-rule var expr)
(if (list? expr)
expr
(if (eqv? var expr)
1
0)))
(define (sum-rule var expr)
(let ((args (cdr expr)))
`(+ ,@(map (lambda (e) (derive var e)) args))))
(define (sub-rule var expr)
(let ((args (cdr expr)))
`(- ,@(map (lambda (e) (derive var e)) args))))
(define (prod-rule var expr)
(let* ((args (cdr expr))
(f (car args))
(g (cadr args)))
`(+ (* ,f ,(derive var g))
(* ,g ,(derive var f)))))
Я могу сделать(derive 'x '(+ (* x x) (* x y)))
и получить (+ (+ (* x 1) (* x 1)) (+ (* x 0) (* y 1)))
, и это правильно. Но мне также хотелось бы программно создавать функции, которые возвращают числовые значения из этих выражений.
Мои попытки не увенчались успехом:
(define (lambda-derive var expr)
(let ([derivative (derive var expr)])
(lambda (var) derivative)))
((lambda-derive 'x '(* x x)) 2) => (+ (* x 1) (* x 1)) ;; should be 4
(define-syntax lbd-macro
(lambda (context)
(syntax-case context ()
[(k expr var )
(with-syntax ([new-var (datum->syntax #'k (syntax->datum #'var))])
#'(lambda (new-var) expr))])))
((lbd-macro (derive 'x '(* x x)) x) 2) => (+ (* x 1) (* x 1)) ;; should be 4
Я чувствую, что упускаю что-то очень очевидное. Может ли кто-нибудь обеспечить свет? (Да, я знаю, что попытки не охватывают многомерные случаи)
==РЕДАКТИРОВАТЬ==
У меня была плохая ночь, и я решил продолжить работу, и я пришел к решению, аналогичному тому, что описал @ignis volens, хотя и с использованием хэш-таблиц и более хакерским подходом:
(define (lambda-aux variables vals expr)
(let ((ht (make-eqv-hashtable (length variables))))
(for-each (lambda (k v) (hashtable-set! ht k v)) variables vals)
(let loop ((expr expr))
(if (list? expr)
(let ((op (car expr))
(args (map loop (cdr expr))))
(cons op args))
(let ((variable (hashtable-ref ht expr #f)))
(if variable
variable
(if (number? expr)
expr
(error "variable not found"))))))))
(define-syntax lambda-derive
(syntax-rules ()
[(_ expr var var* ...)
(lambda (var var* ...) (eval (lambda-aux '(var var* ...) (list var var* ...) (derive 'var 'expr) )))]))
Что можно использовать так:
(define my-test-derivative
;;f(x,y) = x^2 + x * y
;;df/dx (x,y) = 2*x + y
(lambda-derive (+ (* x y) (* x x)) x y))
(my-test-derivative 2 2) => 6
(my-test-derivative 8 2) => 18
;; ...
Это тот случай, когда вы либо хотите стиснуть зубы и использовать eval
:
(eval `(lambda (x) ,(derive 'x '(* x x))) (scheme-report-environment 5))
вернет функцию одного аргумента, которая будет оценивать производную (* x x)
для заданного значения x
(здесь я предполагаю R5RS, поскольку у меня нет Chez Scheme):
> (let ((d (eval `(lambda (x) ,(derive 'x '(* x x))) (scheme-report-environment 5))))
(d 1))
2
Или, что, возможно, лучше (и, конечно, безопаснее), вы можете написать средство оценки символических выражений. Опять же это R5RS:
(define (evaluate-symbolic-expression expression bindings)
;; Evaluate a symbolic expression in the presence of bindings
(cond
((number? expression)
expression)
((symbol? expression)
(let ((found (assq expression bindings)))
(if found
(evaluate-symbolic-expression (cadr found) bindings)
expression)))
((pair? expression)
(apply-symbolic-operator
(car expression)
(map (lambda (e) (evaluate-symbolic-expression e bindings))
(cdr expression))
bindings))
(else
expression)))
(define (all-numbers? l)
(cond
((null? l)
#t)
((number? (car l))
(all-numbers? (cdr l)))
(else
#f)))
(define (apply-symbolic-operator op args bindings)
(if (all-numbers? args)
(let ((f (assq op bindings)))
(if f
(apply (cadr f) args)
`(,op ,@args)))
`(,op ,@args)))
(define arithmetic-operator-bindings
`((+ ,+)
(- ,-)
(* ,*)
(/ ,/)))
И сейчас
> (evaluate-symbolic-expression
(derive 'x '(* x x))
(append '((x 4)) arithmetic-operator-bindings))
8
evaluate-symbolic-expression
можно было бы написать более четко: все началось с реализации вычислителя для того, что по сути было крошечным Lisp-2, но я изменил его и было слишком лень все это переделывать.
Спасибо за вашу помощь! Между утверждением этого вопроса мне удалось решить его так же, как и вам (см. Редактирование). Я думаю, что оставлю этот вопрос открытым на некоторое время, и если никто не вмешается, отметьте ваше сообщение как решение.