【问题标题】:Racket with-hash macro and rename transformersRacket with-hash 宏和重命名转换器
【发布时间】:2013-04-13 23:28:11
【问题描述】:

我创建了这个:

(define-syntax (with-hash stx)
  (syntax-parse stx
    [(_ obj:id ((~or key:id [new-key:id hash-key:id]) ...) body:expr ...+)
     #'(let ([key (hash-ref obj 'key)] ...
             [new-key (hash-ref obj 'hash-key)] ...)
         (begin body ...))]))

所以我可以这样做:

  (require rackunit)
  (define h (hash 'id 1 'name "scott"))
  (with-hash h (id [new-name name])
    (check-equal? id 1)
    (check-equal? new-name "scott"))

如何添加一个替代模式,自动在本地绑定所有哈希键,而无需客户端在调用中指定它们?

即:

(define h (hash 'id 1 'name "scott"))
(with-hash h
  (check-equal? id 1)
  (check-equal? name "scott"))

我怀疑它涉及重命名转换器,但我是否能够根据运行时哈希声明语法参数并动态重命名它们?

另外,我认为这样的事情可能在正确的轨道上:

(define-syntax (with-hash stx)
  (syntax-parse stx
    [(_ obj:id (key:id ...) body:expr ...+)
     #'(let ([key (hash-ref obj 'key)] ...)
         (begin body ...))]
    [(_ obj:id body:expr ...+)
     #'(with-hash obj (id title) body ...)]))

我在哪里调用宏并解析出要绑定的数据,但在这种情况下,id 和 title 变量没有绑定,即使宏可以正常工作。

显然我的理解中遗漏了一些东西。

感谢任何见解。

谢谢。

【问题讨论】:

    标签: macros scheme racket renaming transformer


    【解决方案1】:

    我建议采取另一个方向并坚持提供标识符。在 Scheme 程序的评估环境中创建/添加标识符时总是有点可疑。是的,它是允许的并且可以安全地完成,但它会混淆人们对绑定的内容、时间和地点的理解。

    因此,我建议将您的with-hash 视为一个绑定结构,它允许访问hash 中的字段。像这样使用:

    (with-hash h ((the-id 'id) (the-name 'name)) ...)
    

    或者,使用默认名称,

    (with-hash h (id name) ...)
    

    它会这样实现:

    (define-syntax with-hash
      (syntax-rules ()
        ((_ "gen" hash ((fname fkey) ...) body ...)
         (let ((obj hash))
           (let ((fname (hash-ref obj 'fkey)) ...)
             body ...))))
        ...
        ))
    

    【讨论】:

    • 感谢@GoZoner,我已经编辑了原始帖子以反映允许在绑定中重命名。我同意你的观点,通常我试图做的事情是不合适的,但在这一点上,它更像是一种学习练习。
    • 是的,这是从 JavaScript 进入 with 领域的;除非我们真的知道自己在做什么,否则这不是一个好主意。
    【解决方案2】:

    当我试图想出答案时,Ryan 回复了 :) 无论如何,这是一个带有 eval 的解决方案,具有与其他人已经表达的相同的警告。

    #lang racket
    
    (require (for-syntax syntax/parse))
    
    (define-syntax (with-hash stx)
      (syntax-parse stx 
       [(_ h:expr body:expr ...+)
        #'(begin
            (define-namespace-anchor a)
            (let ([keys (hash-keys h)])
              (define (mk-bind k) `[,k (hash-ref h (quote ,k))])
              (eval 
               `(let ,(map mk-bind keys) 
                  ,@(quote (body ...))) 
               (namespace-anchor->namespace a))))]))
    
    (require rackunit)
    (define h (hash 'id 1 'name "scott"))
    (with-hash h
      (check-equal? id 1)
      (check-equal? name "scott"))
    

    编辑:

    作为替代方案,如果您知道您只会以特定方式使用它,您可以使用类似这样的东西来伪造它。

    #lang racket
    
    (require (for-syntax syntax/parse))
    
    (define-syntax (with-hash stx)
      (syntax-parse stx #:datum-literals (check-equal?)
        [(_ h:expr (check-equal? key:id val:expr) ...)
         #'(let ([keys (hash-keys h)])
             (check-true (hash-has-key? h (quote key))) ...
             (check-equal? (hash-ref h (quote key)) val) ...)]))
    
    (require rackunit)
    (define h (hash 'id 1 'name "scott"))
    (with-hash h
      (check-equal? id 1)
      (check-equal? name "scott"))
    

    【讨论】:

      【解决方案3】:

      你不能,真的。变量作用域是一个静态属性,而哈希的键是一个动态属性,所以任何解决方案都会出错。但是既然你问了,有两个错误的解决方案与你所要求的有点相似。

      您可以做的一件事是使用eval。但是当您调用eval 时,您将丢失任何局部变量;见the docs。您可能可以自己编写代码。

      您可以做的另一件事是通过遮蔽#%top 来更改未绑定变量引用的含义,这是隐式包装对未绑定(或“可能由顶级环境绑定”)变量的变量引用的语法。但这意味着with-hash无法隐藏任何已经具有本地或模块级绑定的键。无论如何,这就是代码的样子:

      (define-syntax (with-hash stx)
        (syntax-case stx ()
          [(with-hash h . body)
           (with-syntax ([#%top (datum->syntax stx '#%top)])
             #'(let-syntax ([#%top
                             (syntax-rules ()
                               [(#%top . x)
                                (hash-ref h 'x)])])
                 (begin . body)))]))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-09
        • 2021-02-24
        • 1970-01-01
        • 1970-01-01
        • 2014-07-07
        • 2012-10-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多