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

How to convert a list to code/lambda in scheme?

假设我有一个列表,例如:(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,然后计算新列表,但这有点难看...或者也许有一个很好的方法来做到这一点?

在 R5RS 方案中(在 Racket 中测试)以下工作:

#lang r5rs

(define foo
  (eval (list 'lambda '(x p) '(+ (* p p) (* x x)))
        (scheme-report-environment 5)))

然后,

(foo 3 4)

;=> 25

eval is to use null-environment 的另一个选项,但 + 未定义。

球拍中的解决方案

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

(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) 结构,实际上不需要额外的宏。

球拍中的复杂解决方案

你也可以在 Racket 中做到这一点:

(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

普通口齿不清

个人比较喜欢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 的宏系统不是很熟悉,所以我使用伟大的 macroexpand-1 在 common lisp 中进行了宏构造,它显示了宏执行的代码构造。然后transferred/guessed对应Racket中的define-syntax-rule.

在 Racket 中,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)))