(require '[taoensso.truss :as truss])假设我们有一张地图描述我的圆圈。圆总是有一个中心,但可以用直径或半径来描述:
{:center [1, 2] :diameter 10}
{:center [1, 2] :radius 5}上述两个圆圈都描述同一个圆圈。
因此,假设我们有一个函数,它期望一个圆映射作为它的输入,我们如何才能最好地用桁架来断言它呢?代码的开头可能如下所示:
(defn circle-tosser
[circle-map]
(truss/have map? circle-map)
(truss/have number? :in (:center circle-map))
(str "Tossing " circle-map))
(circle-tosser {:center [1, 2] :radius 5})
;; => "Tossing {:center [1 2], :radius 5}"当然,问题是我们不能直接断言这些键的值具有certine属性,因为它们不存在。例如,下面的声明将要求同时存在两个键:
(defn circle-tosser
[circle-map]
(truss/have map? circle-map)
(truss/have number? :in (:center circle-map))
(truss/have number? (:diameter circle-map))
(truss/have number? (:radius circle-map))
(str "Tossing " circle-map))
(circle-tosser {:center [1, 2] :diameter 10}) ; Unhandled Exception
(circle-tosser {:center [1, 2] :diameter 10 :radius 5})
;; => "Tossing {:center [1 2], :diameter 10, :radius 5}"然后你就可以开始写这样的东西了
(defn circle-tosser
[circle-map]
(truss/have map? circle-map)
(truss/have number? :in (:center circle-map))
(when (contains? circle-map :diameter)
(truss/have number? (:diameter circle-map)))
(when (contains? circle-map :radius)
(truss/have number? (:radius circle-map)))
(str "Tossing " circle-map))
(circle-tosser {:center [1, 2] :diameter 10})
;; => "Tossing {:center [1 2], :diameter 10}"但这开始变得过于冗长,无法阅读,这是不幸的,因为提供可访问的文档(以代码的形式),是桁架的目的之一。
也许你能想出一个更好的方法来解决这个问题?
发布于 2017-05-11 09:04:15
您可以做的一件事是定义一个函数来隐藏重复的代码,如下
(defn opt-key
[key predicate data]
(when (contains? data key)
(truss/have predicate (get data key))))
(defn circle-tosser
[circle-map]
(truss/have map? circle-map)
(truss/have number? :in (:center circle-map))
(opt-key :diameter number? circle-map)
(opt-key :radius number? circle-map)
(str "Tossing " circle-map))
(circle-tosser {:center [1, 2] :diameter 10})
;; => "Tossing {:center [1 2], :diameter 10}"当然,对于这个例子来说,是引入相互排他性的概念。不过,正如艾伦·汤普森( Alan )所建议的那样,或许更好的做法是将数据正常化。
发布于 2017-05-10 15:45:46
解决你的问题有两种方法。
(1)总是用radius生成圆,而不生成diameter
(2)如果做不到(1),对圆的每个引用都需要包装在一个转换函数中,如下所示:
(defn normalize [c]
(if (contains? c :diameter)
(-> c
(assoc :radius (/ (:diameter c) 2))
(dissoc :diameter))
c))你的代码看起来就像
(defn circle-tosser
[circle-map]
(let [c (normalize circle-map) ]
(truss/have map? c)
(truss/have number? :in (:center c))
(str "Tossing " c)))我总是喜欢解决方案(1),但有时(2)是无法避免的。
https://stackoverflow.com/questions/43896976
复制相似问题