如何以及为什么使用defmulti和defmethod?有什么好处?

时间:2012-10-13 11:08:41

标签: clojure

我想知道为什么defmulti& defmethod用吗? 有什么好处? 你怎么用它?请解释代码中发生的事情。

3 个答案:

答案 0 :(得分:30)

在“一般术语”中,你会把它称为类固醇上的if / else,并且在“令人印象深刻”的风格中你会用“动态调度”,“运行时多态性”等命名它。

假设您要定义一个函数“Add”,它应该适用于各种数据类型,例如在Int的情况下它应该添加数字,如果是字符串,它应该连接字符串。现在使用if else实现它非常简单,基本上你检查参数的类型,如果它们是整数然后添加它们,如果它们是字符串然后连接它们,否则抛出异常。但问题是,如果您想在添加功能中添加对新数据类型的支持,则需要修改添加功能,如果不这样做,则可能无法实现控制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]是我们在defmulti中定义的匿名函数的返回值的排序切换案例,然后是实现。

同样我们可以为字符串

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

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

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

使用与我们的if / else相关的内容调用它,但是对于一个案例尚未实现将导致异常

答案 1 :(得分:10)

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

执行命令时:

(add (int 5) (int 2))

执行'add defmulti'会产生一个列表:

[Integer Integer]

生成它,它会查找上面列出的任何“添加defmethod”,即:

(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")

答案 2 :(得分:0)

Multimethods有助于基于传递的参数调用方法。它可以基于参数的类型或基于参数的某些属性来调用相应的方法。 示例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