需要实施如下的决策表:
MemberType Amount => Discount
"Guest" > 2000 => 3%
"Silver" any => 5%
"Silver" > 1000 => 10%
"Gold" any => 15%
"Gold" > 500 => 20%
我想,如果在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
)
当然,应该有更好的方法来编写/定义这样的规则集。然而,我认为关键是如何定义“defrule”来实现这一目标?
答案 0 :(得分:8)
使用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
答案 1 :(得分:4)
对于此示例,您可以使用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
如果您希望在示例中实现语法,则需要编写宏。一个非常粗略的例子:
(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
答案 2 :(得分:0)
您似乎想要的是以表格形式显示条件,及其相应结果的各种组合的可关联的可视表示形式。
检出cond-table
宏,该宏将表格形式扩展为常规cond
形式:
https://github.com/semperos/rankle/blob/master/src/com/semperos/rankle/util.clj
使用这种方法,您的示例可以这样编码:
(defn discount-for [memtype amount]
(let [spends? (fn [catgy pred] (and (= memtype catgy)
(pred amount)))
>n (fn [n] #(> % n))
any #(< 0 % 501)]
(util/cond-table
:| spends? (>n 2000) (>n 1000) (>n 500) any
:| "Guest" 0.03 0 0 0
:| "Silver" 0.10 0.10 0.05 0.05
:| "Gold" 0.20 0.20 0.20 0.15)))
(discount-for "Guest" 3000)
;;=> 0.03
(discount-for "Gold" 25.95)
;;=> 0.15