22

我想知道为什么要使用 defmulti & defmethod?有什么优势?以及如何使用它?请解释代码中发生了什么。

4

3 回答 3

47

在“一般术语”中,您可以将其称为类固醇上的 if/else,在“打动我”风格中,您可以将其命名为“动态调度”、“运行时多态性”等。

假设您要定义一个函数“Add”,它应该适用于各种数据类型,例如在 Int 的情况下它应该添加数字,在字符串的情况下它应该连接字符串。现在使用 if else 实现它非常简单,基本上你检查参数的类型,如果它们是整数,则添加它们,如果它们是字符串,则连接它们,否则抛出异常。但是这样做的问题是,如果您想在 Add 函数中添加对新数据类型的支持,您将需要修改Add 函数,这在您不控制 Add 源的情况下可能是不可能的,例如它在某些库等中定义的案例, defmultidefmethod允许您解决此问题,即在不修改其代码的情况下向现有函数添加新案例。

(defmulti add (fn [a b] [(type a) (type b)]))

add 是函数名,匿名函数是你的 if/else,基本上这将在你的 add 参数上调用,如果有任何实现,将检查这个函数的返回值。现在让我们为 Integer 实现它。

(defmethod add [Integer Integer] ([a b] (+ a b)))

[Integer Integer]是在我们定义的匿名函数的返回值上排序 switch case,defmulti然后是实现。

同样我们可以为字符串做

(defmethod add [String String] ([a b] (str a b)))

用整数调用它 (add (int 1) (int 2))

用字符串调用它 (add "hello" "world")

使用与我们的 if/else 不匹配的东西调用它,即尚未实现案例将导致异常

于 2012-10-13T12:06:09.813 回答
14

根据我在阅读答案(并使用上面提供的示例)后观察到的情况,以下内容也有助于理解:

当你执行命令时:

(add (int 5) (int 2))

发生的事情是执行了“add defmulti”,从而产生了一个列表:

[Integer Integer]

生成后,它会查找上面列表中标识的任何“添加定义方法”,即:

(add [Integer Integer])

并传递它收到的论点。

定义多方法以使参数传递清晰的另一种方法是:

(defmulti add (fn [a b] [(type a) (type b)]))
(defmethod add [Integer Integer] [a b] (+ a b))
(defmethod add [String String] [a b] (str a b))

然后将函数运行为:

(add 12 73)

或者

(add "thank" "you")
于 2012-10-13T13:47:09.830 回答
0

多方法有助于根据传递的参数调用方法。它可以基于参数的类型/或基于参数的某些属性调用相应的方法。示例 1 基于类型

test=> (defmulti multiply (fn [a b] [(type a) (type b)]))
#'test/multiply
test=> (defmethod multiply [Integer Integer] [a b] (* a b))
#object[clojure.lang.MultiFn 0x189f7b45 "clojure.lang.MultiFn@189f7b45"]
test=> (defmethod multiply [String String] [a b] (str a b))
#object[clojure.lang.MultiFn 0x189f7b45 "clojure.lang.MultiFn@189f7b45"]
test=> (multiply 10 10)
test=> (multiply (int 10) (int 10))
100
test=> (multiply "10" "10")
"1010"

示例 2 基于属性

test=> (defmulti mn (fn[a b] ( < a b ) ))
#'test/mn
test=> (defmethod mn true [a b] (+ a b) )
#object[clojure.lang.MultiFn 0x700f6a44 "clojure.lang.MultiFn@700f6a44"]
test=> (defmethod mn false [a b] (- a b) )
#object[clojure.lang.MultiFn 0x700f6a44 "clojure.lang.MultiFn@700f6a44"]
test=> (mn 1 2)
3
test=> (mn 2 1)
1
test=> (mn 1 1)
0
于 2018-11-12T11:12:48.080 回答