(define-macro slambda
(lambda (args body)
`(let ((self (lambda ,args ,body)))
self)))
你好,我对这个宏有一个“问题”用于自引用lambda ..它有效,但是当我想从外面引用“self”时失败...意味着第一个应用程序有效,第二个不能< / p>
((slambda(x)(+ x 1))10)
((slambda()self))
答案 0 :(得分:1)
也许这会更好地替换let
letrec
,如下所示:
(define-macro slambda
(lambda (args body)
`(letrec ((self (lambda ,args ,body)))
self)))
在Scheme中,您有词法范围,self
在let
的正文之前不起作用。在let的主体中调用self
的过程本身并未由该名称定义。你可能更容易看到你是否let
:
((lambda (self) ...)
(lambda () self)) ; self referenced outside procedure that defines it
请注意define-macro
不是标准的方案语法,因此您应该指定正在使用的实现。幸运的是,这个问题与宏无关。
答案 1 :(得分:1)
如果您正在使用方案,那么最好使用标准的define-syntax而不是不常用的define-macro。使用define-syntax,您必须使用datum-&gt;语法来使宏不受干扰地操作并将名称“self”注入输出语法。这是您的代码翻译为define-syntax,与guile一起测试:
(define-syntax slambda
(lambda (x)
(syntax-case x ()
[(slambda formals body0 body1 ...)
(with-syntax ([self (datum->syntax #'slambda 'self)])
#'(letrec ([self (lambda formals body0 body1 ...)])
self))])))
答案 2 :(得分:0)
Sylwester的回答是正确的,但我想提出一个更大的观点:除非您的Scheme实现不提供卫生的程序宏系统,否则没有充分理由使用define-macro
。
对于回指宏(例如您要编写的宏),如果您使用支持它的Scheme实现(例如Racket或Guile),最好使用语法参数。这是一个球拍示例:
#lang racket
(provide slambda self)
(require racket/stxparam srfi/31)
(define-syntax-parameter self
(lambda (stx)
(raise-syntax-error 'self "Can only be used inside slambda")))
(define-syntax slambda
(syntax-rules ()
((_ params body ...)
(rec (ohai . params)
(syntax-parameterize ((self (make-rename-transformer #'ohai)))
body ...)))))
当然,正如您在我的示例中所看到的,我使用了rec
。在您想要自我引用程序的一般情况下,最好使用rec
;您只需指定要引用该过程的名称(而不是使用硬编码的self
)。由于rec
不是照应用的,因此它的定义要简单得多:
(define-syntax rec
(syntax-rules ()
((_ (id . params) body ...)
(rec id (lambda params body ...)))
((_ id value)
(letrec ((id value)) id))))
您可以像这样使用它(在这种情况下,我使用recur
作为自引用;当然,您可以选择任何您喜欢的名称):
(define nested-length
(rec (recur x)
(cond ((null? x) 0)
((pair? x) (+ (recur (car x)) (recur (cdr x))))
(else 1))))
答案 3 :(得分:0)
您需要引用 lambda-part ,在那里您可以将其分配给 self 。
(define-macro slambda
(lambda (arg1 . arg2)
`(let ((self '(slambda ,arg1 ,@arg2)))
(lambda ,arg1 ,@arg2))))
如果你想将它与多个参数一起使用,那么就需要点和 unquote-splicing 。