递归实体规范

Fra*_*ard 6 recursion clojure clojure.spec

我还没有找到任何关于如何执行递归实体规范的示例,就像我在下面尝试一样.我意识到::left并且::right因为它们尚未定义而失败,所以我想知道如何在::node规范中递归地定义它们.

(s/def ::key string?)
(s/def ::value string?)
(s/def ::left ::node)
(s/def ::right ::node)
(s/def ::n int?)
(s/def ::node (s/keys :req [::key ::value ::n]
                      :opt [::left ::right]))

(defn test-it []
  (s/valid? ::node
            {::key "hi"
             ::value "bye"
             ::n 0
             ::left {::key "what"
                     ::value "nothing"
                     ::n 0}
             ::right {::key "hello"
                      ::value "goodbye"
                      ::n 0}
             }))
Run Code Online (Sandbox Code Playgroud)

Jos*_*osh 1

您所拥有的不是左右实体,而是以相同方式定义的两个节点,不幸的是您不能在映射中拥有两个具有相同名称的键,因为规范不允许将关键字“别名”到规范,而是使用关键字本身来标识规范。

如果您愿意,一种选择是根据单个::children键定义左节点和右节点,该键是(一个或)两个 s 的集合::node。

(s/def ::key string?)
(s/def ::value string?)
(s/def ::n int?)

(s/def ::node (s/keys :req [::key ::value ::n]))
(s/def ::children (s/coll-of ::node :count 2))
;; for 1 or 2 children:   (s/coll-of ::node :min-count 1 :max-count 2)

(s/valid? ::node
  {::key "parent-1" ::value "parent-1" ::n 1
   ::children [{::key "leaf-1" ::value "leaf-1" ::n 2}
               {::key "parent-2" ::value "parent-2" ::n 3
                ::children [{::key "leaf-2" ::value "leaf-2" ::n 4}
                            {::key "leaf-3" ::value "leaf-3" ::n 5}]}]})
Run Code Online (Sandbox Code Playgroud)

这为您提供了类似的结构,但包含两个节点的向量(而不是两个键,每个键都有一个节点)稍微增加了复杂性。

允许纯粹根据其本身进行定义的另一种选择是放弃映射结构,并改为使用嵌套向量:

(s/def ::node (s/or :parent (s/coll-of ::node :count 2)
                    :leaf (s/tuple ::key ::value ::n)))

(s/valid? ::node
  [[[["a" "a" 1]
     ["b" "b" 2]]
    ["c" "c" 3]]
   ["d" "d" 4]])
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为元素是连续的,不需要与唯一键关联,如上面的映射结构所示(是的,向量也是关联的,但在本例中使用了它们的顺序性质)。诚然,这并不那么“干净”,第一种方法可能是首选,但如果您愿意放弃关联结构并将其换成顺序结构,那么它是一种选择。