2013-05-02 28 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

相关问题