如何在Clojure中实现决策矩阵/表

Alf*_*iao 3 clojure

需要实现如下的决策表:

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"来实现这一目标?

Dan*_*eal 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
Run Code Online (Sandbox Code Playgroud)


A. *_*ebb 5

对于此示例,您可以使用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)