Clojureは、すでにfn
の条件の前と後に対応しています(文書化されていない可能性があります)。
user> (defn divide [x y]
{:pre [(not= y 0)]}
(/ x y))
user> (divide 1 0)
Assert failed: (not= y 0)
[Thrown class java.lang.Exception]
醜いですが。
私はたぶんマクロを書いて、どのテストが簡潔な方法で失敗したのかを報告することができます(引用してテストをそのまま印刷します)。リンクしたCLコードは、その巨大なcase文でかなり厄介に見えます。私の考えでは、ここではマルチメソッドが良いでしょう。あなたはこのようなものをかなり簡単に一緒に投げることができます。
(defmacro assert* [val test]
`(let [result# ~test] ;; SO`s syntax-highlighting is terrible
(when (not result#)
(throw (Exception.
(str "Test failed: " (quote ~test)
" for " (quote ~val) " = " ~val))))))
(defmulti validate* (fn [val test] test))
(defmethod validate* :non-zero [x _]
(assert* x (not= x 0)))
(defmethod validate* :even [x _]
(assert* x (even? x)))
(defn validate [& tests]
(doseq [test tests] (apply validate* test)))
(defn divide [x y]
(validate [y :non-zero] [x :even])
(/ x y))
その後:あなたは、自動的にこのように、ブロック内で定義された任意の関数にテストを追加する言語を変更したい場合は、マクロが必要になります
user> (divide 1 0)
; Evaluation aborted.
; Test failed: (not= x 0) for x = 0
; [Thrown class java.lang.Exception]
user> (divide 5 1)
; Evaluation aborted.
; Test failed: (even? x) for x = 5
; [Thrown class java.lang.Exception]
user> (divide 6 2)
3
ニース;それを知らなかった – pmf
別の素晴らしい答えをありがとう。これは私がやりたいこととほぼ同じです。 また、マクロがより良い解決策である理由の1つを示しています。関数が2つ以上の同じ特性、たとえば2つの正の整数を必要とする関数の場合、問題の引数を明確にする方法について考えてみましょう。マクロルートを使用すると、値だけでなく問題の引数の名前をエラーメッセージに入れることができます。私はあなたが関数でそれを行うことはできないと思います。 – clartaq
前提条件と事後条件がここに記載されています。http://clojure.org/special_forms条件マップを検索します。 – Chouser