Как преобразовать список в код / ​​лямбду в схеме?

Допустим, у меня есть список, например: (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.

Другие вопросы по тегам