构造Clojure defmulti / defmethod

时间:2011-12-17 15:58:02

标签: clojure

我错过了关于defmulti和defmethod的重要观点。我读过几本书对defmulti的解释,我仍然很困惑。

我希望得到一个随机值,具体取决于它是否为交易或金额为100.00

我想调用(random-val)并返回avail-trans值或随机小数。我已经尝试将函数放在一个映射中,但是我得到了相同的值,用于avail-trans,一个\ B.B。

(def^:dynamic map-val {:trans (random-trans) :amt (random-amount)})

这是显示我正在做的不起作用的最小代码量。我很感激任何指示或帮助。

(def^:dynamic avail-trans [\B \W \D \A])

(defn random-trans 
    [] 
    (nth avail-trans (.nextInt random (count avail-trans))))

(defn random-amount
    []
    (float (/ (.nextInt random (count (range 1 10000))) 25 )))

以下构造不正确,但我不确定为什么或如何解决问题:

(defmulti random-val :val-type)

(defmethod random-val :trans []
    (random-trans))

(defmethod random-val :amt []
    (random-amount))

调用(random-val :trans)会导致此错误:

java.lang.IllegalArgumentException:multimethod'random-val'中没有方法用于调度值:null(NO_SOURCE_FILE:0)

2 个答案:

答案 0 :(得分:7)

使用defmulti创建多方法;你这样做了。 defmulti需要名称和调度函数(以及文档字符串,如果您愿意,还有一些选项,但请忘记这些选项)。

(defmulti random-val identity) 

当你使用defmethod实现多方法时,你需要指定你正在实现的多方法的名称,它应该匹配的调度值,然后是函数tail(arglist加上你想要它做的任何事情) )。

(defmethod random-val :trans [t] (random-trans))
(defmethod random-val :amt [t] (random-amt))

您收到java.lang.IllegalArgumentException: No method in multimethod 'random-val' for dispatch value: null (NO_SOURCE_FILE:0)因为当您分配的random-val:val-type的发送功能应用于任何其他关键字时,它会为您提供null。当Clojure试图查找匹配该调度值的方法时,它会失败。

但是即使它没有在那里失败,你定义的方法也有0个arity(没有值),所以你也需要修复它(在上面完成)。

最后,这似乎不适合协议。只需使用两个单独的函数random-amountrandom-trans

另请注意,Clojure's website对多方法有很好的解释。

答案 1 :(得分:1)

每次使用“\B”时,您都会获得相同的值,因为您在地图map-val中将其关联时正在评估该函数,从而绑定值B永远反对map-val中的键':trans',而不是函数randon-trans本身。

如果在map-val中删除功能分配周围的parens,它将正常工作。然后不需要多方法,这可能不适合@ isaac-hodes建议。

这在REPL中对我有用:

(def avail-trans [\B \W \D \A])
(def random (java.util.Random.))

(defn random-trans []
  (nth avail-trans (.nextInt random (count avail-trans))))

(defn random-amount []
  (float (/ (.nextInt random (count (range 1 10000))) 25 )))

; No parens around function names
(def map-val {:trans random-trans :amt random-amount})

(println ((:trans map-val)))
(println ((:amt map-val)))