如何将列表转换为方案中的code/lambda?

人气:181 发布:2022-10-16 标签: lisp s-expression scheme racket

问题描述

假设我有一个列表,例如:(define a '(+ (* p p) (* x x)))

如何使用a给出的表达式定义过程,例如: (define (H x p) (+ (* p p) (* x x))))

我尝试这样做:(define (H x p) (eval a)),但它显示px是未定义的。我想,对于apply或类似的东西,有一个简单的解决方法,但我不能理解它。

我想我可以根据传递给过程H的值修改我的列表中的px,然后计算新的列表,但它有点难看...或者也许有一个很好的方法来做到这一点?

推荐答案

球拍解决方案

您尝试做的实际上是将预定义的函数体列表结构注入到函数定义(宏)调用体中。

(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)))

281