为具有跨层约束的嵌套数据结构创建Clojure规范生成器

时间:2017-05-11 15:42:35

标签: clojure clojure.spec

我正在使用Clojure规范来规范一个简单的数据结构:

{:max 10
 :data [[3 8 1]
        [9 0 1]]}

:data值是从零到:max值(包括0和(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创建自定义生成器,但没有管理。我不知道如何表达跨越嵌套结构层的约束(键::image约束其他地方的向量中的值)。

是否可以创建一个生成{{1}} s的Clojure规范/ test.check生成器?

1 个答案:

答案 0 :(得分:5)

当然!这里的关键是创建域的模型。在这里,我认为模型是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返回一个新的生成器,它以所需的形状创建地图。我们在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,因为生成器可以尝试生成非常大的随机随机样本值。需要绑定这样的东西在自定义生成器中很常见。

通过更多的努力,您可以从这些规格和生成器部件中获得更多的重用。