是否有可用于比较模式宏形式的工具/函数?

时间:2013-05-02 01:47:13

标签: clojure

我在我的一些宏中使用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库或其他模式匹配库中是否有某些内容可以执行此操作?

2 个答案:

答案 0 :(得分:7)

实际测试宏的扩展是非常脆弱的。如果沿着这条路走下去,宏中的任何微小变化都会导致测试失败 - 即使你的宏仍然做同样的事情!

更好的方法 - IMO - 是测试你的宏应该做什么。我们可以放心地假设调用宏有一个可观察的副作用 - 在你的例子中它设置了一个JS对象的属性。

在这种情况下,我不是测试扩展,而是编写一个测试来确保JS对象的状态是你在调用宏之后所期望的。

这将测试与实现分离,让您可以自由地重构宏,因为测试更加强大,并且只有在宏实际上做错了事情时才会失败。

根据经验,我从不测试宏的扩展。

答案 1 :(得分:1)

我现在已经完成了自己的工作。它模式匹配矢量,列表和哈希映射值(集合上的模式匹配和哈希映射键对我来说太难了)。

(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