【问题标题】:set! global from Scheme macro?放!来自 Scheme 宏的全局?
【发布时间】:2011-07-27 11:02:57
【问题描述】:

我正在尝试为define 编写一个包装器,用于存储传递给它的值。我一直在逐步接近它(一般来说是 Lisp 的新手,甚至是 Scheme 的新手),但遇到了障碍。

在 Racket 中,我开始:

> (require (lib "defmacro.ss"))
> (define-macro (mydefine thing definition)
      `(define ,thing ,definition))
> (mydefine a 9)
> a
9

好的,这行得通。在返回 s-exprs 之前,是时候在宏中做一些事情了:

> (define-macro (mydefine thing definition)
    (display "This works")
    `(define ,thing ,definition))
> (mydefine a "bob")
This works
> a
"bob"

很好。但是我一辈子都不能让它设置一个全局变量而不是显示一些东西:

> (define *myglobal* null)
> (define-macro (mydefine thing definition)
    (set! *myglobal* "This does not")
    `(define ,thing ,definition))
> (mydefine a ":-(")
set!: cannot set identifier before its definition: *myglobal*

任何有关如何完成此任务的建议将不胜感激。

我怀疑我在这里试图逆流而上,要么通过摆弄 Scheme 中宏的全局变量,要么使用 define-macro 而不是学习用于创建宏的 Scheme 特定语法。

【问题讨论】:

    标签: macros lisp scheme racket


    【解决方案1】:

    语句(set! *myglobal* "This does not")是在变压器环境中执行的,而不是普通环境。所以它找不到*myglobal。我们需要在定义*myglobal* 的环境中执行这两个表达式。

    这是一种解决方案:

    (define *defined-values* null)
    
    (define-macro (mydefine thing definition)  
      `(begin
         (set! *defined-values* (cons ,definition *defined-values*))
         (define ,thing ,`(car *defined-values*))))
    
    
    > (mydefine a 10)
    > (mydefine b (+ 20 30))
    > a
    10
    > b
    50
    > *defined-values*
    (50 10)
    > (define i 10)
    > (mydefine a (begin (set! i (add1 i)) i)) ;; makes sure that `definition` 
                                               ;; is not evaluated twice.
    > a
    11
    

    如果 Scheme 实现不提供define-macro 但有define-syntax,则mydefine 可以定义为:

    (define-syntax mydefine
      (syntax-rules ()
        ((_ thing definition)
         (begin       
           (set! *defined-values* (cons definition *defined-values*))
           (define thing (car *defined-values*))))))
    

    【讨论】:

      【解决方案2】:

      您正在与 Racket 的 阶段分离 竞争——这意味着每个阶段(运行时和编译时)在不同的世界中运行。正如 Vijay 所说,解决这个问题的一种方法是在运行时做你想做的事,但从长远来看,这可能不是你需要的。问题是尝试这些事情通常意味着您将希望在编译时级别存储一些句法信息。例如,假设您要存储所有已定义名称的名称,以便在第二个宏中使用,该宏会将它们全部打印出来。你可以这样做(我在这里使用理智的宏,define-macro 是一个不应该用于实际工作的遗留黑客,你可以在guide 中查找这些东西,然后在@987654322 @):

      #lang racket
      (define-for-syntax defined-names '())
      (define-syntax (mydefine stx)
        (syntax-case stx ()
          [(_ name value)
           (identifier? #'name)
           (begin (set! defined-names (cons #'name defined-names))
                  #'(define name value))]
          ;; provide the same syntactic sugar that `define' does
          [(_ (name . args) . body)
           #'(mydefine name (lambda args . body))]))
      

      注意defined-names是在语法级别定义的,这意味着正常的运行时代码无法引用它。事实上,您可以在运行时级别将其绑定到不同的值,因为这两个绑定是不同的。现在已经完成了,您可以编写使用它的宏——即使defined-names 在运行时不可访问,它也是语法级别的普通绑定,所以:

      (define-syntax (show-definitions stx)
        (syntax-case stx ()
          [(_) (with-syntax ([(name ...) (reverse defined-names)])
                 #'(begin (printf "The global values are:\n")
                          (for ([sym (in-list '(name ...))]
                                [val (in-list (list name ...))])
                            (printf "  ~s = ~s\n" sym val))))]))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-30
        • 2012-10-24
        • 2010-09-12
        • 1970-01-01
        • 2016-02-03
        相关资源
        最近更新 更多