【发布时间】:2019-10-17 08:05:48
【问题描述】:
我是 clojure 的新手,我非常喜欢它。现在,我正在尝试加强并使用该语言的更多功能:multimethods 和 protocols
我已经看到很多博客文章、文档和问题都涉及到这个主题,但我仍然不确定我是否明白。
假设我有一个函数可以根据输入参数的类型改变行为。我可以通过 multimethod 或 protocol 来实现该功能:
;; Multi
(defmulti foo class)
(defmethod foo java.lang.Double [x]
"A double (via multimethod)")
(defmethod do-a-thing java.lang.Long [x]
"A long (via multimethod)")
;; Protocol
(defprotocol Bar
(bar [x] "..."))
(extend-protocol Bar
java.lang.Double
(bar [x] "A double (via protocol)")
java.lang.Long
(bar [x] "A long (via protocol)"))
两者都有效,似乎协议方法更适合速度目的。
但是,我想实现一个取决于输入参数的数学函数(例如面积)。一种方法是使用 case,但我觉得它不干净。
所以我尝试了 multimethod 版本。它就像一个魅力:
(defmulti area (fn [shape & _]
shape))
(defmethod area :square
[_ x]
(* x x))
(defmethod area :circle
[_ r]
(* r r Math/PI))
(defmethod area :triangle
[_ b h]
(* 1/2 b h))
但是下面的协议实现不起作用:
(defprotocol Surface
(surface [x] 0.0))
(extend-protocol Surface
:square
(surface [x] (* x x))
:circle
(surface [r] (* r R Math/PI))
:triangle
(surface [b h] (* 1/2 b h)))
我收到以下错误:
Execution error (ClassCastException) at user/eval2081 (REPL:1).
class clojure.lang.Keyword cannot be cast to class java.lang.Class (clojure.lang.Keyword is in unnamed module of loader 'bootstrap'; java.lang.Class is in module java.base of loader 'bootstrap')
我的问题是:
有没有办法用协议实现这个功能,或者这是多方法唯一的问题?
协议是否仅适用于基于类型的识别?就像这里所说的https://stackoverflow.com/a/8074581/1537744?
【问题讨论】:
标签: clojure