Как преобразовать список в код / лямбду в схеме?
Допустим, у меня есть список, например:
(define a '(+ (* p p) (* x x)))
.
Как я могу определить процедуру с выражением, заданным
a
, например здесь:
(define (H x p) (+ (* p p) (* x x))))
?
Я пробовал сделать что-то вроде:
(define (H x p) (eval a))
но он говорит, что и не определены. Я думаю, есть простой способ обхода
apply
или что-то подобное, но я не могу об этом думать.
Думаю, я мог бы просто изменить
p
а также
x
в моем списке в соответствии со значением, переданным в процедуру
H
а затем оценить новый список, но это некрасиво ... Или, может быть, есть хороший способ сделать это?
2 ответа
Решение в Racket
На самом деле вы пытаетесь внедрить конструкцию списка тела предопределенной функции в тело вызова определения функции (макроса).
(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
происходит на глобальном уровне окружающей среды, не следует опасаться побочных эффектов.
Ниже приведены мои более сложные решения с
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
В общем шепеляв
Лично я предпочитаю общую макросистему 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, используя отличный, который показывает конструкцию кода, выполняемую макросом. А потом передал / угадал соответствующий
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)))
В схеме R5RS (проверено в Racket) работает следующее:
#lang r5rs
(define foo
(eval (list 'lambda '(x p) '(+ (* p p) (* x x)))
(scheme-report-environment 5)))
Потом,
(foo 3 4)
;=> 25
Другой вариант для eval
использовать null-environment
, но потом
+
появляется undefined.