2013-04-20 88 views
3

我創造了這個:球拍與哈希宏和重命名變壓器

(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和標題變量不受約束,即使宏觀以其他方式工作。

很明顯,我錯過了我的理解。

任何見解都值得讚賞。

謝謝。

回答

1

你不能,真的。變量範圍是一個靜態屬性,而哈希鍵是一個動態屬性,所以任何解決方案都會出錯。但是,既然你問了,有兩個錯誤的解決方案與你所要求的模糊不清。

你可以做的一件事是使用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)))])) 
+0

謝謝瑞安.... – Scott 2013-04-21 23:14:42

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 ...)))) 
    ... 
    )) 
+1

感謝@GoZoner,我編輯了原文,以反映允許在綁定中重命名。我同意你的觀點,即我一直試圖做的事情是不合適的,但這比現在的任何事都更像是一次學習活動。 – Scott 2013-04-20 22:17:10

+1

是的,這是從JavaScript進入''''領域;除非我們真的知道我們在做什麼,否則這不是一個好主意。 – dyoo 2013-04-21 01:32:49

1

蕩,瑞安迴應,而我試圖想出一個答案:)這裏是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"))