clojure.spec coll-of替代类型

时间:2017-06-17 13:41:51

标签: clojure clojure.spec

我正在使用clojure.spec来验证地图条目的向量。矢量看起来像:

*.ts

我想将规范结构化为需要1..N [{:point {:x 30 :y 30}} {:point {:x 34 :y 33}} {:user "joe"}] 个条目,并且只需要一个::point条目。

这是我(不成功)尝试构建此规范:

::user

当我只运行一个(s/def ::coord (s/and number? #(>= % 0))) (s/def ::x ::coord) (s/def ::y ::coord) (s/def ::point (s/keys :req-un [::x ::y])) (s/def ::user (s/and string? seq)) (s/def ::vector-entry (s/or ::pt ::user)) (s/def ::my-vector (s/coll-of ::vector-entry :kind vector)) 条目的验证时,它可以工作:

::point

有关如何构建spec> (s/valid? ::point {:point {:x 0 :y 0}}) true spec> (s/valid? ::my-vector [{:point {:x 0 :y 0}}]) false 部分的任何想法,以便向量条目可以是s/or::user类型吗?

此外,有关如何在向量中仅需要一个::point条目和1..N ::user条目的任何想法?

3 个答案:

答案 0 :(得分:4)

以下是您问题中数据的可能规范:

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

(s/def ::coord nat-int?)
(s/def ::x ::coord)
(s/def ::y ::coord)
(s/def ::xy (s/keys :req-un [::x ::y]))
(s/def ::point (s/map-of #{:point} ::xy))
(s/def ::username (s/and string? seq))
(s/def ::user (s/map-of #{:user} ::username))

(s/def ::vector-entry (s/or :point ::point :user ::user))
(s/def ::my-vector (s/coll-of ::vector-entry :kind vector))

(s/valid? ::point {:point {:x 0 :y 0}})
(s/valid? ::my-vector [{:point {:x 0 :y 0}}])
(s/valid? ::my-vector [{:point {:x 0 :y 0}} {:user "joe"}])

一些观察结果:

  • or规范要求为规格命名。
  • 按类型:point:user对不同项目进行标记需要间接级别,我在顶部使用map-of,在嵌套级别使用keys但是有很多选择
  • 您的规范中的小错误可能会在trying each subform at the REPL提前发现。
  • 在这种情况下,指出数据的相对难度暗示这种数据形状对于程序也是不方便的。当您知道需要:user时,为什么强制程序进行O(N)搜索?

希望这有帮助!

答案 1 :(得分:4)

虽然斯图尔特的答案非常有启发性并解决了许多你的问题,但我认为它不符合你确保“只有一个::user条目”的标准。

重复他的回答:

(s/def ::coord nat-int?)
(s/def ::x ::coord)
(s/def ::y ::coord)
(s/def ::xy (s/keys :req-un [::x ::y]))
(s/def ::point (s/map-of #{:point} ::xy))
(s/def ::username (s/and string? seq))
(s/def ::user (s/map-of #{:user} ::username))

(s/def ::vector-entry (s/or :point ::point 
                            :user ::user))
(s/def ::my-vector (s/and (s/coll-of ::vector-entry
                                     :kind vector)
                          (fn [entries]
                            (= 1
                               (count (filter (comp #{:user}
                                                    key)
                                              entries))))))

(s/valid? ::point {:point {:x 0 :y 0}})
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}])
;; => false
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
                       {:user "joe"}])
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
                       {:point {:x 1 :y 1}}
                       {:user "joe"}])
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
                       {:user "joe"}
                       {:user "frank"}])
;; => false

重要的补充是::my-vector的规范。请注意,s/or的一致输出是一个映射条目,这是传递给新自定义谓词的内容。

我应该注意到,虽然这有效,但它会为您的验证添加另一个线性扫描。不幸的是,我不知道spec是否提供了一次通过的好方法。

答案 2 :(得分:2)

蒂姆和斯图亚特的答案解决了这个问题并且提供了非常丰富的信息。我想指出的是,Clojure规范还有一个功能,可用于指定点向量和用户的结构。

即,spec允许使用正则表达式来指定序列。有关更多信息,请参阅the Spec guide

以下是使用序列规格的解决方案。这建立在以前的解决方案之上。

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

(s/def ::coord nat-int?)
(s/def ::x ::coord)
(s/def ::y ::coord)
(s/def ::xy (s/keys :req-un [::x ::y]))
(s/def ::point (s/map-of #{:point} ::xy))
(s/def ::username (s/and string? seq))
(s/def ::user (s/map-of #{:user} ::username))

(s/def ::my-vector (s/cat :points-before (s/* ::point)
                          :user ::user
                          :points-after (s/* ::point)))

(s/valid? ::point {:point {:x 0 :y 0}})
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}])
;; => false
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
                       {:user "joe"}])
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
                       {:point {:x 1 :y 1}}
                       {:user "joe"}])
;; => true
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
                       {:user "joe"}
                       {:user "frank"}])
;; => false
(s/valid? ::my-vector [{:point {:x 0 :y 0}}
                       {:user "joe"}
                       {:point {:x 1 :y 1}}])
;; => true

如果最后需要::user条目,则可以很容易地进行调整。