【问题标题】:Scheme self-reference lambda macro方案自引用 lambda 宏
【发布时间】:2014-04-19 07:51:45
【问题描述】:
(define-macro slambda
  (lambda (args body)
    `(let ((self (lambda ,args ,body)))
        self)))

你好,我有一个关于自引用 lambda 的宏的“问题”。它可以工作,但是当我想从外部引用“self”时失败了。这意味着第一个应用程序有效,第二个没有

  1. ((slambda (x) (+ x 1)) 10)

  2. ((slambda () self))

【问题讨论】:

  • 感谢您提出这个问题!感谢您,我通过简单的搜索找到了自己问题的答案。

标签: macros lambda scheme


【解决方案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 不是标准方案语法,因此您应该指定您正在使用的实现。幸运的是,这个问题与宏无关。

【讨论】:

  • 是的,我将它切换到 letrec 并且某些情况下开始工作,但我停在这个应用程序 ((slambda () (slambda () self) self)) .. 我猜它应该可以工作
  • @Poody 您的slambda 宏不能正确处理多个主体形式,这是您遇到的问题。
  • @Chris Jester-Young 所以我需要更深入吗?
  • @Poody 您需要让您的宏为主体采用多个参数。尽管如此,我仍然相信define-macro 是解决这个问题的错误方法。如果您使用的是 Racket,则可以使用我的答案。如果您使用不同的实现,最好只使用rec
  • @Poody 我同意 Chris 的观点,但你使用它的方式可能是你可以改用命名为 let
【解决方案2】:

如果您使用的是方案,则最好使用标准的定义语法而不是不总是支持的定义宏。使用 define-syntax,您必须使用 datum->syntax 来让宏不卫生地运行,并将名称“self”注入到输出语法中。这是你的代码翻译成定义语法,用 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))])))

【讨论】:

  • 这看起来比我预期的要干净得多 - 感谢分享!
【解决方案3】:

你需要引用你quasiqute的lambda-part,以便它可以分配给self

(define-macro slambda
  (lambda (arg1 . arg2)
    `(let ((self '(slambda ,arg1 ,@arg2)))
       (lambda ,arg1 ,@arg2))))

如果您想将它与多个参数一起使用,则需要在此处使用点和 unquote-splicing

【讨论】:

    【解决方案4】:

    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))))
    

    【讨论】:

      猜你喜欢
      • 2021-01-19
      • 1970-01-01
      • 2014-12-21
      • 2018-05-17
      • 1970-01-01
      • 2018-07-14
      • 2023-04-02
      • 2013-01-18
      • 1970-01-01
      相关资源
      最近更新 更多