4

我创建了这个:

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

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

IE:

(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 变量也不会绑定。

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

任何见解都值得赞赏。

谢谢。

4

3 回答 3

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 ...))))
    ...
    ))
于 2013-04-20T20:55:48.467 回答
1

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

您可以做的一件事是使用eval. 但是当您调用时,eval您将丢失任何局部变量;请参阅文档。您可能可以自己编写代码。

您可以做的另一件事是通过 shadowing 更改未绑定变量引用的含义#%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)))]))
于 2013-04-21T05:28:48.383 回答
1

当我试图想出一个答案时,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"))
于 2013-04-21T05:58:50.280 回答