我想知道为什么defmulti& defmethod用吗? 有什么好处? 你怎么用它?请解释代码中发生的事情。
答案 0 :(得分:30)
在“一般术语”中,你会把它称为类固醇上的if / else,并且在“令人印象深刻”的风格中你会用“动态调度”,“运行时多态性”等命名它。
假设您要定义一个函数“Add”,它应该适用于各种数据类型,例如在Int的情况下它应该添加数字,如果是字符串,它应该连接字符串。现在使用if else实现它非常简单,基本上你检查参数的类型,如果它们是整数然后添加它们,如果它们是字符串然后连接它们,否则抛出异常。但问题是,如果您想在添加功能中添加对新数据类型的支持,则需要修改添加功能,如果不这样做,则可能无法实现控制Add的来源,例如它在某些库中定义的情况等。
defmulti
和defmethod
允许您解决此问题,即在不修改代码的情况下向现有函数添加新案例。
(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