首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用桃仁索桁架验证地图中可选密钥下的内容

用桃仁索桁架验证地图中可选密钥下的内容
EN

Stack Overflow用户
提问于 2017-05-10 15:33:29
回答 2查看 32关注 0票数 0
代码语言:javascript
复制
(require '[taoensso.truss :as truss])

假设我们有一张地图描述我的圆圈。圆总是有一个中心,但可以用直径或半径来描述:

代码语言:javascript
复制
{:center [1, 2] :diameter 10}
{:center [1, 2] :radius   5}

上述两个圆圈都描述同一个圆圈。

因此,假设我们有一个函数,它期望一个圆映射作为它的输入,我们如何才能最好地用桁架来断言它呢?代码的开头可能如下所示:

代码语言:javascript
复制
(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属性,因为它们不存在。例如,下面的声明将要求同时存在两个键:

代码语言:javascript
复制
(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}"

然后你就可以开始写这样的东西了

代码语言:javascript
复制
(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}"

但这开始变得过于冗长,无法阅读,这是不幸的,因为提供可访问的文档(以代码的形式),是桁架的目的之一。

也许你能想出一个更好的方法来解决这个问题?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-05-11 09:04:15

您可以做的一件事是定义一个函数来隐藏重复的代码,如下

代码语言:javascript
复制
(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 )所建议的那样,或许更好的做法是将数据正常化。

票数 0
EN

Stack Overflow用户

发布于 2017-05-10 15:45:46

解决你的问题有两种方法。

(1)总是用radius生成圆,而不生成diameter

(2)如果做不到(1),对圆的每个引用都需要包装在一个转换函数中,如下所示:

代码语言:javascript
复制
(defn normalize [c]
  (if (contains? c :diameter)
    (-> c
      (assoc :radius (/ (:diameter c) 2))
      (dissoc :diameter))
    c))

你的代码看起来就像

代码语言:javascript
复制
(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)是无法避免的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43896976

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档