为具有跨层约束的嵌套数据结构创建 Clojure 规范生成器
Creating a Clojure spec generator for a nested data structure with constraints across layers
我正在使用 Clojure 规范来规范一个简单的数据结构:
{:max 10
:data [[3 8 1]
[9 0 1]]}
:data
值是从零到 :max
值(含)的区间内由等长整数向量组成的向量。我用规范表示如下:
(s/def ::max pos-int?)
(s/def ::row (s/coll-of nat-int? :kind vector?, :min-count 1))
(s/def ::data (s/and (s/coll-of ::row :kind vector?, :min-count 1)
#(apply = (map count %))))
(s/def ::image (s/and (s/keys :req-un [::max ::data])
(fn [{:keys [max data]}]
(every? #(<= 0 % max) (flatten data)))))
自动生成器对于前三个规格工作正常,但对于 ::image
则不然。 (s/exercise ::image)
尝试 100 次后总是失败。
我试图为 ::image
创建一个自定义生成器,但没有成功。我不知道如何表达跨嵌套结构层的约束(键 :max
约束其他地方的向量中的值)。
是否可以创建一个 Clojure spec/test。检查生成 ::image
s 的生成器?
当然!这里的关键是创建领域模型。这里我认为模型是max、col-size和row-size。这足以生成一个有效的示例。
所以像这样:
(def image-gen
(gen/bind
(s/gen (s/tuple pos-int? (s/int-in 1 8) (s/int-in 1 8)))
(fn [[max rows cols]]
(gen/hash-map
:max (s/gen #{max})
:data (gen/fmap #(into [] (partition-all cols) %)
(s/gen (s/coll-of (s/int-in 0 (inc max))
:kind vector?
:count (* rows cols))))))))
首先,我们生成一个[<max-value> <rows> <cols>]
的元组。 gen/bind
然后是 returns 一个新的生成器,可以创建所需形状的地图。我们在内部嵌套 gen/fmap
以构建所有随机数据值的向量,然后将其重新整形为适当的嵌套向量形式。
然后您可以将其组合成图像:
(s/def ::image
(s/with-gen
(s/and (s/keys :req-un [::max ::data])
(fn [{:keys [max data]}]
(every? #(<= 0 % max) (flatten data))))
(fn [] image-gen)))
可能需要注意的一件有趣的事情是,我将行数和列数限制为不超过 7,否则生成器可能会尝试生成非常大的随机随机样本值。需要绑定这样的东西在自定义生成器中很常见。
通过更多的努力,您还可以更好地重用其中的一些规范和生成器部分。
我正在使用 Clojure 规范来规范一个简单的数据结构:
{:max 10
:data [[3 8 1]
[9 0 1]]}
:data
值是从零到 :max
值(含)的区间内由等长整数向量组成的向量。我用规范表示如下:
(s/def ::max pos-int?)
(s/def ::row (s/coll-of nat-int? :kind vector?, :min-count 1))
(s/def ::data (s/and (s/coll-of ::row :kind vector?, :min-count 1)
#(apply = (map count %))))
(s/def ::image (s/and (s/keys :req-un [::max ::data])
(fn [{:keys [max data]}]
(every? #(<= 0 % max) (flatten data)))))
自动生成器对于前三个规格工作正常,但对于 ::image
则不然。 (s/exercise ::image)
尝试 100 次后总是失败。
我试图为 ::image
创建一个自定义生成器,但没有成功。我不知道如何表达跨嵌套结构层的约束(键 :max
约束其他地方的向量中的值)。
是否可以创建一个 Clojure spec/test。检查生成 ::image
s 的生成器?
当然!这里的关键是创建领域模型。这里我认为模型是max、col-size和row-size。这足以生成一个有效的示例。
所以像这样:
(def image-gen
(gen/bind
(s/gen (s/tuple pos-int? (s/int-in 1 8) (s/int-in 1 8)))
(fn [[max rows cols]]
(gen/hash-map
:max (s/gen #{max})
:data (gen/fmap #(into [] (partition-all cols) %)
(s/gen (s/coll-of (s/int-in 0 (inc max))
:kind vector?
:count (* rows cols))))))))
首先,我们生成一个[<max-value> <rows> <cols>]
的元组。 gen/bind
然后是 returns 一个新的生成器,可以创建所需形状的地图。我们在内部嵌套 gen/fmap
以构建所有随机数据值的向量,然后将其重新整形为适当的嵌套向量形式。
然后您可以将其组合成图像:
(s/def ::image
(s/with-gen
(s/and (s/keys :req-un [::max ::data])
(fn [{:keys [max data]}]
(every? #(<= 0 % max) (flatten data))))
(fn [] image-gen)))
可能需要注意的一件有趣的事情是,我将行数和列数限制为不超过 7,否则生成器可能会尝试生成非常大的随机随机样本值。需要绑定这样的东西在自定义生成器中很常见。
通过更多的努力,您还可以更好地重用其中的一些规范和生成器部分。