#macros #scheme #lisp
#макросы #схема #lisp
Вопрос:
Для проекта колледжа мы работаем над learning scheme, однако нам пришлось выполнить сложное задание с небольшими знаниями. Нам предоставляются определенные функции, такие как «‘let», «‘cond», «and'» и т.д., И нас просят добавить макросы.
(define eval
(λ (e env)
(cond ((symbol? e) (lookup-var e env))
((not (list? e)) e) ; non-list non-symbol is self evaluatory
;; special forms go here:
((equal? (car e) 'λ) ; (λ VARS BODY)
(let ((vars (cadr e)) (body (caddr e)))
(list '%closure vars body env)))
((equal? (car e) 'if)
(eval_ (if (eval_ (cadr e) env) (caddr e) (cadddr e)) env))
((equal? (car e) 'quote) (cadr e))
;; Add More Macros Here:
;; ((equal? (car e) 'let) xxx)
;;((equal? (car e) 'cond) xxx)
;;((equal? (car e) 'and) xxx)
;((equal? (car e) 'or) xxx)
(else (let ((eeoe (map (λ (e0) (eval_ e0 env)) e)))
(apply_ (car eeoe) (cdr eeoe)))))))
Итак, в основном нас просят заполнить пробелы, где находятся комментарии ‘;;’.
Я попытался выполнить часть ‘cond и получил это
((equal? (car e) 'cond)
(env (cdr e) env))
Но я понятия не имею, правильно ли это (очень мало знаний о схеме). Любая помощь в выяснении этого будет высоко оценена. Спасибо.
Ответ №1:
На самом деле вы добавляете не макросы, а специальные формы. Если вы знаете, что let
это просто синтаксический сахар для анонимного вызова функции. например.
(let ((a expr1) (b expr2)) body ...)
поддерживается в вашем evaluator уже, если вы изменяете и оцениваете:
((lambda (a b) body ...) expr1 expr2)
Чтобы заставить вас работать let
, работает следующим образом:
(let ((bindings (cadr e))
(body (cddr e)))
(eval_ `((lambda ,(map car bindings) ,@body) ,@(map cadr bindings))))
Теперь реальные макросы, которые вы бы ввели в новый тип %closure
, чтобы всякий раз, когда вы находите его в качестве operator, вы привязывали символы, а не их оценку, запускали с ним вычислитель, как если бы это была функция, а затем выполняли _eval
с результатом. Тогда вместо реализации cond
и let
вы могли бы просто добавить функцию о том, как переписать ее на то, что вы уже поддерживаете. Таким образом, вы могли бы сделать let
вот так:
((lambda (let)
(let ((a expr1) (b expr2))
(cons a b)))
(~ (bindings . body) `((lambda ,(map car bindings) ,@body) ,@(map cadr bindings))))
Предполагается, что у вас есть quasiquote и map
, но это может быть легко реализовано без них с помощью более подробного кода. ~
просто выбирается случайным образом, чтобы быть версией макроса λ
. При вычислении это создает, возможно, %mclosure
структуру, и вам нужно обрабатывать ее специально, чтобы не оценивать ее аргументы, а оценивать результат. С этого момента вы могли бы поддерживать специальные формы, предварительно определив %mclosure
в среде загрузки.