问题描述
假设我有一个列表,例如:(define a '(+ (* p p) (* x x)))
。
a
给出的表达式定义过程,例如:
(define (H x p) (+ (* p p) (* x x))))
?
我尝试这样做:(define (H x p) (eval a))
,但它显示p
和x
是未定义的。我想,对于apply
或类似的东西,有一个简单的解决方法,但我不能理解它。
H
的值修改我的列表中的p
和x
,然后计算新的列表,但它有点难看...或者也许有一个很好的方法来做到这一点?
推荐答案
球拍解决方案
您尝试做的实际上是将预定义的函数体列表结构注入到函数定义(宏)调用体中。
(define expr '(+ (* p p) (* x x)))
(eval `(define (fun x p) ,expr))
如果您去掉(eval ...)
层,您会看到:
`(define (fun x p) ,expr)
;; '(define (fun x p) (+ (* p p) (* x x)))
代码,实际由eval
求值。
由于这eval
发生在全球环境层面,因此不需要担心任何副作用。
define-expr
宏的更复杂的解决方案。
他们解释了为什么这个问题这么难解决。
在这之后,我发现实际上只需要一个(eval `(define (<args>) ,expr)
构造,而实际上不需要附加宏。
球拍中的复杂解决方案
你也可以在球拍上这样做:
(define expr '(+ (* p p) (* x x)))
(define-syntax-rule (define-expr args expr)
(define args expr))
(eval `(define-expr (fun x p) ,expr))
此调用在后台执行:
(define (fun x p) (+ (* p p) (* x x)))
您尝试做的实际上是一个动态宏。 这样做的问题是,您必须将 运行时中的函数体代码。 不知何故,您需要对函数体表达式进行一次求值,而不是宏的其他求值。 因此,有必要将宏调用
(eval `<macrocall ,component-to-be-evaluated-once-more>)
我将此构造称为eval-over-macro-call
。
之后,您可以调用如此定义的fun
函数:
(fun 3 4) ;; 25
在通用LISP中
就我个人而言,我更喜欢Common Lisp的宏系统。更直接。
(defparameter *expr* '(+ (* p p) (* x x)))
(defmacro defun-expr (fnname args expr)
`(defun ,fnname ,args ,expr))
(macroexpand-1 '(defun-expr fun (x p) *expr*)) ;; doesn't work
;; (DEFUN FUN (X P) *EXPR*) ;
;; T
;; you can see, that *EXPR* actually has to be evaluated once more
(macroexpand-1 `(defun-expr fun (x p) ,*expr*)) ;; this however is correct
;; (DEFUN FUN (X P) (+ (* P P) (* X X)))
;; Thus when you call the macro, you have to execute it using eval:
(eval `(defun-expr fun (x p) ,*expr*))
;; FUN ;; function is defined!
(fun 3 4) ;; 25
因为我不太熟悉racket的宏系统,所以我使用了常用的lisp来进行宏构造,使用的是Greatmacroexpand-1
,它显示宏执行的代码构造。然后在球拍中调用/猜测对应的define-syntax-rule
。
在球拍中,macroexpand-1
为(syntax->datum (expand-once '<macrocall>))
:
(syntax->datum (expand-once `(define-expr (fun x p) ,expr)))
;; '(define (fun x p) (+ (* p p) (* x x)))