2017-05-10 46 views
0
(require '[taoensso.truss :as truss]) 

假設我們有一張描述i circle的地圖。的圓始終具有中心,但是可以通過任一的直徑或半徑來描述:使用Taoensso 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性質,因爲它們不存在neccicerily的。例如下面的聲明將要求這兩個鍵是存在在同一時間:

(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}" 

但開始變得過於冗長,無法讀取,這因爲提供了可訪問的文檔(以代碼的形式),這是不幸的,是桁架目的的一部分。

也許你可以想出一個更好的方法來解決這個問題?

回答

0

有一兩件事你可以做的是定義一個函數遁形重複的代碼,比如當然

(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 Thompson所建議的,將數據標準化可能會更好。

1

有兩種方法可以解決您的問題。

(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)不能避免。

+0

我明白你的觀點對圓圈的例子是否有效。也許我通過使用非現實世界的例子做錯了。想一想,例子是「我們有一個配置圖,它有許多不同的可選鍵,我們要驗證這些值」,就像leiningen project.clj。你認爲我應該用這個例子來發表一個新帖子嗎? – Rovanion