【问题标题】:A Clojure Spec that matches and generates an ordered vector of variable length匹配并生成可变长度的有序向量的 Clojure Spec
【发布时间】:2017-04-05 11:58:47
【问题描述】:

让我们从常规序列开始

(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]]] ...)

我们还可以尝试使用spec/and::cat 添加附加条件:

(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

但遗憾的是无法生成自己的数据,因为 spec/cat 的生成器只生成当然不符合 vector? 谓词的列表:

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

总结一下:如何编写既能接受又能生成像[hi "there"][my "dear" :friend] 这样的向量的规范?

也可以将问题改写为“spec/cat 是否有替代方案可以生成向量而不是列表?”或“是否可以将 :kind 参数传递给spec/cat?”或“我可以将生成器附加到一个规范,该规范将原始生成器的输出转换为向量吗?”。

【问题讨论】:

    标签: clojure clojure.spec


    【解决方案1】:

    独立于规范创建正则表达式模式:

    (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])
    

    【讨论】:

    • 我对这个解决方案有疑问。我们正在失去“平坦” ``` (s/de​​f ::pattern (s/cat :sym symbol? :str string? :kws (s/* keyword?))) (s/de​​f ::pattern-2 (s /cat :s string? :p ::pattern)) (s/valid? ::pattern ['af "5" :key]) ;; true (s/valid?::pattern-2 ["string" 'af "5" :key]) ;; true (s/de​​f ::pattern-3 (s/cat :s string?:p ::solution)) (s/valid?::pattern-3 ["string" 'af "5" :key]) ;;错误的 !!!
    • 是的,这是上述方法的一个问题 - 使用 s/and 会将 ::solution 从正则表达式操作更改为规范,并且只有正则表达式操作可以以“平面”方式组合。目前没有一种简单的解决方案可以满足仅矢量猫的所有需求。
    • @AlexMiller 如何在生成器中强制转换为递归向量?例如生成嵌套的 Hiccup 结构。
    【解决方案2】:

    事实证明,截至clojure-1.9.0-alpha15,没有简单的方法可以解决这个问题。一种可能的解决方案是修改生成器,将 cat 给出的序列转换为如下向量:

    (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]}
    

    【讨论】:

      【解决方案3】:

      要添加 Alex 的解决方案,这里有一个宏,它定义了 vector-cat 正则表达式操作:

      (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 ~@key-pred-forms))
           #(gen/fmap vec (spec/gen (spec/cat ~@key-pred-forms)))))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多