Macros 具有哈希宏和重命名转换器的Racket

Macros 具有哈希宏和重命名转换器的Racket,macros,scheme,racket,renaming,transformer,Macros,Scheme,Racket,Renaming,Transformer,我创造了这个: (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

我创造了这个:

(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-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变量没有绑定,即使宏在其他情况下工作

很明显,我的理解中遗漏了一些东西

任何见解都将不胜感激


谢谢。

我建议换个方向,坚持提供标识符。在Scheme程序的评估环境中创建/添加标识符时,总是有点可疑。是的,这是允许的,可以安全地完成,但它混淆了人们对什么是约束、何时何地的理解

因此,我建议将您的
和hash
看作一个绑定构造,它允许访问
hash
中的字段。这样使用:

(with-hash h ((the-id 'id) (the-name 'name)) ...)
(define-syntax with-hash
  (syntax-rules ()
    ((_ "gen" hash ((fname fkey) ...) body ...)
     (let ((obj hash))
       (let ((fname (hash-ref obj 'fkey)) ...)
         body ...))))
    ...
    ))
或者,使用默认名称

(with-hash h (id name) ...)
其实施方式如下:

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

你不能,真的。变量作用域是一个静态属性,而散列的键是一个动态属性,所以任何解决方案都是错误的。但自从你问起,有两个错误的解决方案与你的要求大致相似

您可以使用
eval
。但是当您调用
eval
时,您将丢失任何局部变量;看见你也许可以自己编写代码

您可以做的另一件事是通过隐藏
#%top
来更改未绑定变量引用的含义,这是隐藏在未绑定(或“可能由顶级环境绑定”)变量的变量引用周围的语法。但这意味着使用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)))]))

Dang,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"))

谢谢@GoZoner,我编辑了原始帖子以反映允许在绑定中重命名。我同意你的看法,通常我尝试做的是不合适的,但在这一点上,这更像是一个学习练习,而不是任何东西;除非我们真的知道自己在做什么,否则这不是一个好主意。