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)
您所拥有的不是左右实体,而是以相同方式定义的两个节点,不幸的是您不能在映射中拥有两个具有相同名称的键,因为规范不允许将关键字“别名”到规范,而是使用关键字本身来标识规范。
如果您愿意,一种选择是根据单个::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)
这是有效的,因为元素是连续的,不需要与唯一键关联,如上面的映射结构所示(是的,向量也是关联的,但在本例中使用了它们的顺序性质)。诚然,这并不那么“干净”,第一种方法可能是首选,但如果您愿意放弃关联结构并将其换成顺序结构,那么它是一种选择。