【发布时间】:2020-12-12 21:10:14
【问题描述】:
对于递归定义的数据结构中节点中的值之间的依赖关系,是否有规范或惯用的方式来编写规范?
作为一个最小的例子,假设我想将排序列表存储为嵌套向量,其中每个“节点”是一个值和列表的尾部:
[1 [2 [3 [4 nil]]]]
可以编写列表本身结构的规范
(s/def ::node (s/or :empty nil?
:list (s/cat :value number? :tail ::node)))
但是,当我想添加订购要求时,我找不到一个好的方法 写吧。
直截了当的写法感觉有点笨拙。作为符合
:tail 的值是 MapEntry,我不能使用像 (get-in % [:tail :list :value]) 这样的东西
(我可以把它写成(get-in % [:tail 1 :value]),但是那个硬编码的索引感觉太脆弱了),
但必须通过(val):
(s/def ::node (s/or :empty nil?
:list (s/& (s/cat :value number? :tail ::node)
#(or (= (-> % :tail key) :empty)
(< (:value %) (-> % :tail val :value)))
)))
这行得通:
(s/conform ::node nil) ; [:empty nil]
(s/conform ::node [1 nil ] ) ; [:list {:value 1, :tail [:empty nil]}]
(s/explain ::node [4 [1 nil]] )
; {:value 4, :tail [:list {:value 1, :tail [:empty nil]}]} - failed:
; (or (= (-> % :tail key) :empty) (< (:value %) (-> % :tail val
; :value))) in: [1] at: [:list] spec: :spec-test.core/node
; [4 [1 nil]] - failed: nil? at: [:empty] spec: :spec-test.core/node
(s/conform ::node [1 [4 nil]] ) ; [:list {:value 1, :tail [:list {:value 4, :tail [:empty nil]}]}]
(s/conform ::node [1 [2 [4 nil]]] )
; [:list
; {:value 1,
; :tail
; [:list {:value 2, :tail [:list {:value 4, :tail [:empty nil]}]}]}]
或者,我可以使用多规范为::node 制定规范
更清楚一点:
(s/def ::node (s/or :empty nil?
:list (s/& (s/cat :value number? :tail ::node)
(s/multi-spec empty-or-increasing :ignored)
)))
这也允许我分离出:empty 分支,但它仍然存在获取:tail 的(头)值的问题:
(defmulti empty-or-increasing #(-> % :tail key))
(defmethod empty-or-increasing :empty
[_]
(fn[x] true))
(defmethod empty-or-increasing :default
[_]
#(do (< (:value %) (-> % :tail val :value)))
)
有没有办法获得:tail 节点的:value 而不必
用#(-> % :tail val :value) 提取MapEntry 的val 部分
或#(get-in % [:tail 1 :value])?
【问题讨论】:
标签: clojure destructuring clojure.spec