2013-05-02 30 views
3

我用我的一些宏的gensym功能,則使得它很難測試:是否有可用於比較模式宏表單的工具/函數?

所以一些宏觀的擴張可能是:

'(let [G__10458 (js-obj)] 
    (aset G__10458 "a" (fn [] (? G__10458.val))) 
    (aset G__10458 "val" 3) G__10458) 

我要的是測試它匹配了這種類型的彭定康:

'(let [%1 (js-obj)] 
    (aset %1 "a" (fn [] (? %1.val))) 
    (aset %1 "val" 3) %1) 

有什麼東西在clojure.core.match庫或做這個另一種模式匹配庫?

+0

首先,寫出您的宏沒有gensym。繼續更新和測試宏,直到它做你想做的事。最後,添加gensym。 – WolfeFan 2013-05-02 14:22:02

+0

我知道我可以做到這一點,但這不是我想要的 – zcaudate 2013-05-02 21:00:24

+0

gensym是一種工具,當您的宏已經過了測試階段並且您將其投入到實際工作中時可以使用它。 gensym存在的全部原因是爲了防止在運行時命名衝突,當您仍在構建和測試宏時,這不是主要關心的問題。 – WolfeFan 2013-05-03 02:28:32

回答

1

我已經推出了我自己的現在。它在向量,列表和哈希映射vals上匹配(模式匹配和散列映射鍵現在對我來說太難了)。

(defn match-sym? [v] 
    (and (symbol? v) 
     (re-find #"^%" (str v)))) 

(defn match 
    ([f1 f2] (match f1 f2 (atom {}))) 
    ([v1 v2 dict] 
    (cond (or (and (list? v1) (list? v2)) 
      (and (vector? v1) (vector? v2))) 
     (and (= (count v1) (count v2)) 
      (->> (map #(match %1 %2 dict) v1 v2) 
        (every? true?))) 

     (and (hash-map? v1) (hash-map? v2)) 
     (and (= (keys v1) (keys v2)) 
      (->> (map #(match %1 %2 dict) (vals v1) (vals v2)) 
        (every? true))) 

     (match-sym? v2) 
     (if-let [vd (@dict v2)] 
      (match v1 vd dict) 
      (do (swap! dict assoc v2 v1) 
       true)) 
     :else (= v1 v2)))) 

及其用法:

> (match '(1 1) '(1 1)) 
;=> true 

> (match '(1 1) '(%1 %1)) 
;=> true 

> (match '(1 2) '(%1 %1)) 
;=> false 

> (match '(let [x 1] (+ x 2)) 
     '(let [%x 1] (+ %x 2))) 
;=> true 

> (match '(let [G__42879 (js-obj)] 
      (aset G__42879 "a" (fn [] (? G__42879.val))) 
      (aset G__42879 "val" 3) G__42879) 

     '(let [%x (js-obj)] 
      (aset %x "a" (fn [] (? %x.val))) 
      (aset %x "val" 3) %x)) 
;=> true 
+0

不錯,你也可以做到這一點,並與core.unify或core.logic的簡單的統一接口處理更多的案例 – dnolen 2013-05-06 12:31:47

+0

你可以舉個例子嗎? – zcaudate 2013-05-06 20:46:58

7

實際測試您的宏的擴展是非常脆弱的。如果你沿着這條路走下去,宏中的任何小改動都會導致你的測試失敗 - 即使你的宏仍然做同樣的事情!

更好的方法 - 海事組織 - 是測試你的宏應該做什麼。我們可以安全地假設調用你的宏有一個可觀察的副作用 - 在你的例子中它設置了一個JS對象的屬性。

在這種情況下,我不會測試擴展,而是編寫一個測試,確保JS對象的狀態與調用宏後所期望的一致。

這將測試與實現分離開來,讓您可以自由地重構宏,因爲您認爲合適,因爲測試更健壯,而且只會在宏發生錯誤時纔會失敗。作爲一條經驗法則,我從不測試宏的擴展。

+0

我通常把我的宏分解成更小的函數,所以我真的在測試那些較小的代碼生成函數。我發現,在推理它會做什麼之前,先弄清楚它的輸出代碼是否更好。而且在這種情況下,它的輸出clojurescript,這真的很難直接測試。 – zcaudate 2013-05-02 01:58:06

+0

這很公平。然而,測試擴展仍然脆弱,所以我個人不會這樣做。對我來說,就像測試一個'double'函數一樣,確保它使用參數和數字2調用'*',而一個完全有效的實現可以只是'(+參數參數)'。即使輸出完全一樣,這也會破壞你的測試。只是我的2c。 – leonardoborges 2013-05-02 02:04:21

相關問題