需要实现如下的决策表:
MemberType Amount => Discount
"Guest" > 2000 => 3%
"Silver" any => 5%
"Silver" > 1000 => 10%
"Gold" any => 15%
"Gold" > 500 => 20%
Run Code Online (Sandbox Code Playgroud)
我想,如果在Clojure中正确实现,我们可以定义一个规则表,如下所示:
(defrule calc-discount
[member-type amount]
"Guest" (greater-than 2000) => 0.03
"Silver" (anything) => 0.05
"Silver" (greater-than 1000) => 0.1
"Gold" (anything) => 0.15
"Gold" (greater-than 500) => 0.2
)
Run Code Online (Sandbox Code Playgroud)
当然,在编写/定义这样的规则集时应该有更好的方法.然而,我认为关键是如何定义"defrule"来实现这一目标?
使用core.match!它是用于模式匹配的Clojure库.
你的例子会变成这样的东西......
(let [member-type "Gold"
amount 600]
(match [member-type amount]
["Guest" (_ :guard #(> % 2000))] 0.03
["Silver" (_ :guard #(> % 1000))] 0.1
["Silver" _] 0.05
["Gold" (_ :guard #(> % 500))] 0.2
["Gold" _] 0.15
:else 0))
; => 0.2
Run Code Online (Sandbox Code Playgroud)
对于此示例,您可以使用condp.
(defn discount
[member-type amount]
(condp (fn [[type tier] _] (and (= member-type type) (> amount tier))) nil
["Guest" 2000] 0.03
["Silver" 1000] 0.10
["Silver" 0] 0.05
["Gold" 500] 0.20
["Gold" 0] 0.15
0.00))
(discount "Gold" 600) ;=> 0.2
Run Code Online (Sandbox Code Playgroud)
如果您希望实现示例中的语法,则需要编写一个宏。一个非常粗略的例子:
(defmacro defrule [name fields & clauses]
(let [exp (fn [f c] (if (list? c) (list* (first c) f (rest c)) (list `= c f)))]
`(defn ~name ~fields
(cond
~@(for [clause (partition-all (+ 2 (count fields)) clauses)
form [(cons `and (map exp fields clause)) (last clause)]]
form)))))
(def any (constantly true))
(defrule calc-discount
[member-type amount]
"Guest" (> 2000) => 0.03
"Silver" (> 1000) => 0.10
"Silver" (any) => 0.05
"Gold" (> 500) => 0.20
"Gold" (any) => 0.15)
(calc-discount "Silver" 1234) ;=> 0.10
Run Code Online (Sandbox Code Playgroud)