2017-04-05 40 views
4

的有序向量允许在开始时的甲Clojure的规格相匹配,并生成可变长度

(require '[clojure.spec  :as spec] 
     '[clojure.spec.gen :as gen]) 
(spec/def ::cat (spec/cat :sym symbol? :str string? :kws (spec/* keyword?))) 

正规序列的载体

(spec/conform ::cat '[af "5"]) 
=> {:sym af, :str "5"} 
(spec/conform ::cat '[af "5" :key]) 
=> {:sym af, :str "5", :kws [:key]} 

而且列表

匹配
(spec/conform ::cat '(af "5")) 
=> {:sym af, :str "5"} 
(spec/conform ::cat '(af "5" :key)) 
=> {:sym af, :str "5", :kws [:key]} 

如果我们想限制这个,我们可以尝试使用spec/tuple;但遗憾的是它只匹配固定长度的矢量,即它需要在至少一个空列表是元组的最后一部分:

(spec/def ::tuple (spec/tuple symbol? string? (spec/* keyword?))) 
(spec/conform ::tuple '[af "5"]) 
=> :clojure.spec/invalid 
(spec/exercise ::tuple) 
=> ([[r ""()] [r "" []]] [[kE "" (:M)] [kE "" [:M]]] ...) 

我们也可以尝试添加一个额外条件::catspec/and

(spec/def ::and-cat 
    (spec/and vector? (spec/cat :sym symbol? :str string? :kws (spec/* keyword?)))) 

它匹配精细

(spec/conform ::and-cat '[af "5"]) 
=> {:sym af, :str "5"} 
(spec/conform ::and-cat '[af "5" :key]) 
=> {:sym af, :str "5", :kws [:key]} 
(spec/conform ::and-cat '(af "5" :key)) 
=> :clojure.spec/invalid 

但遗憾的是未能在产生自发电机它自己的数据只生产这当然不符合vector?谓词列表:

(spec/exercise ::and-cat) 
=> Couldn't satisfy such-that predicate after 100 tries. 

所以总结:一个人如何写一个测试,这是双方能够接受和产生像[hi "there"][my "dear" :friend]载体?

也可以将问题改为“是否有替代spec/cat的替代产生向量”?或者“是否有可能将一个友好的论点传递给spec/cat?”或者“我可以将发生器附加到一个规格中,该规格将原始发生器的输出并将其转换为矢量?”。

回答

2

创建一个从规格独立于正则表达式模式:

(require '[clojure.spec :as s] '[clojure.spec.gen :as gen]) 

(def pattern 
    (s/cat :sym symbol? :str string? :kws (s/* keyword?))) 

(s/def ::solution 
    (s/with-gen (s/and vector? pattern) 
       #(gen/fmap vec (spec/gen pattern)))) 

(s/valid? ::solution '(af "5" :key)) ;; false 

(s/valid? ::solution ['af "5" :key]) ;; true 

(gen/sample (s/gen ::solution) 4) 
;; ([m ""] [. "" :Q] [- "" :?-/-9y :_7*/!] [O._7l/.?*+ "z" :**Q.tw.!_/+!gN :wGR/K :n/L]) 
+0

我有一个问题,这个解决方案。我们失去'平坦' ''' (s/def :: pattern(s/cat:sym symbol?:str string?:kws(s/* keyword?))) (s/def :: pattern-2(s/cat:s字符串?:p :: pattern)) (s/valid?:: pattern ['af“5”:key]);; true (s/valid?:: pattern-2 [“string”'af“5”:key]);; (s/def :: pattern-3(s/cat:s string?:p :: solution)) (s/valid?:: pattern-3 [“string”'af“5”:key ]);;假! < - 我们失去平淡 (s/valid?:: pattern-3 [“string”['af“5”:key]]);;真的! < - 我们正在失去平原 ''' – Lambder

+1

是的,这是上述方法的一个问题 - 使用s /并将改变::从正则表达式的解决方案从一个规范和只有正则表达式操作可以合并在一个“平“ 办法。目前没有一种简单的解决方案可以满足只有矢量猫的所有需求。 –

+0

@AlexMiller如何在生成器中将强制转换为向量进行递归?例如生成嵌套的Hiccup结构。 – Roman

0

clojure-1.9.0-alpha15开始解决这个问题的方法并不简单。一个可能的解决方案是修改发电机投由猫中给出的序列到像载体:

(spec/def ::solution 
    (let [s (spec/cat :sym symbol? :str string? :kws (spec/* keyword?))] 
    (spec/with-gen s #(gen/fmap vec (spec/gen s))))) 

然后我们可以看到两个产生并接受正确的数据:

(spec/exercise ::solution) 
=> ([[T ""] {:sym T, :str ""}] 
    [[t* "Z" :g*] {:sym t*, :str "Z", :kws [:g*]}] 
    [[G?8 "td" :*K/j] {:sym G?8, :str "td", :kws [:*K/j]}]) 

虽然它有一个问题,规范没有验证该输入是一个向量,它接受序列如列表:

(spec/conform ::solution '(N-G.?8?4/- "" :G7y_.?Gx_/Oy1Dv :g!/Ooh0 :N-??h/o+cN)) 
=> {:sym N-G.?8?4/-, :str "", :kws [:G7y_.?Gx_/Oy1Dv :g!/Ooh0 :N-??h/o+cN]} 
0

要在Alex的解决方案添加,这里是它定义了一个矢量猫正则表达式操作的宏:

(defmacro vcat 
    "Takes key+pred pairs, e.g. 

    (vcat :e even? :o odd?) 

    Returns a regex op that matches vectors, returning a map containing 
    the keys of each pred and the corresponding value. The attached 
    generator produces vectors." 
    [& key-pred-forms] 
    `(spec/with-gen (spec/and vector? (spec/cat [email protected])) 
    #(gen/fmap vec (spec/gen (spec/cat [email protected])))))