Clojure规范,匹配并生成可变长度的有序向量
让我们从规则的顺序开始Clojure规范,匹配并生成可变长度的有序向量,clojure,clojure.spec,Clojure,Clojure.spec,让我们从规则的顺序开始 (需要“[clojure.spec:as spec] “[clojure.spec.gen:as gen]) (spec/def::cat(spec/cat:sym-symbol?:str-string?:kws(spec/*关键字?)) 哪个匹配向量 (规范/符合::cat'[af“5”]) =>{:sym af,:str“5”} (规格/一致性::cat'[af“5”:键]) =>{:sym af,:str“5”,:kws[:key]} 还有名单 (规格/一致性:
(需要“[clojure.spec:as spec]
“[clojure.spec.gen:as gen])
(spec/def::cat(spec/cat:sym-symbol?:str-string?:kws(spec/*关键字?))
哪个匹配向量
(规范/符合::cat'[af“5”])
=>{:sym af,:str“5”}
(规格/一致性::cat'[af“5”:键])
=>{:sym af,:str“5”,:kws[:key]}
还有名单
(规格/一致性::类别(af“5”))
=>{:sym af,:str“5”}
(规格/一致性::类别(af“5”:键))
=>{:sym af,:str“5”,:kws[:key]}
如果我们想限制这一点,我们可以尝试使用spec/tuple
;但遗憾的是,它只匹配固定长度的向量,即它至少需要一个空列表作为元组的最后一部分:
(spec/def::tuple(spec/tuple符号?字符串?(spec/*关键字?)
(spec/conform::tuple'[af“5”])
=>:clojure.spec/invalid
(规范/练习::元组)
=>([[r”“()][r”“[]][[kE”“(:M)][kE”“[:M]]]]…)
我们还可以尝试将附加条件添加到::cat
,使用spec/和
:
(规范/定义::和目录
(规格/矢量?(规格/类别:符号?:str字符串?:kws(规格/*关键字?))
哪一个配得好
(规范/符合::和类别'[af“5”])
=>{:sym af,:str“5”}
(规范/符合::和类别“[af“5”:键])
=>{:sym af,:str“5”,:kws[:key]}
(规格/符合::和类别(af“5”:键))
=>:clojure.spec/invalid
但遗憾的是,由于spec/cat
的生成器只生成当然不符合vector?
谓词的列表,因此无法生成自己的数据:
(规范/练习::和cat)
=>尝试100次后无法满足该谓词。
总结一下:如何编写一个既能接受又能生成向量的规范,比如[hi“there”]
[my“亲爱的”:friend]
你也可以将这个问题重新表述为“除了生成向量而不是列表的
spec/cat
之外,还有其他方法吗?”或者“是否可以将:kind参数传递给spec/cat
?”或者“我可以将一个生成器附加到一个获取原始生成器输出并将其强制转换为向量的规范?”.Turs从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]}
独立于规范创建正则表达式模式:
(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])
为了补充Alex的解决方案,这里有一个宏,它定义了向量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)))))
我对这个解决方案有问题。我们正在失去“平面度”````(s/def::pattern(s/cat:sym symbol?:str string?:kws(s/*关键字?)))(s/def::pattern-2(s/cat:s string?:p::pattern))(s/valid?::pattern['af“5”:key]);;正确(s/valid?::模式2[“字符串”af“5”:键];;true(s/def::pattern-3(s/cat:s string?:p::solution))(s/valid?::pattern-3[“string”'af“5”:key]);;错误!!!是的,这是上述方法的一个问题-使用s/and将::解决方案从正则表达式op更改为规范,并且只有正则表达式op可以以“平面”方式组合。目前还没有一个简单的解决方案可以满足纯向量cat的所有需求。@AlexMiller在生成器中,如何将强制转换为递归的向量?例如,生成嵌套的Hiccup结构。