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

Phi*_*hil 17 clojure

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

Ank*_*kur 32

在"一般术语"中,你会称它为类固醇的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]是关于我们定义的匿名函数的返回值defmulti然后执行的sort switch case .

同样我们可以为字符串做

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

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

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

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

  • 非常感谢你!这非常有帮助! (3认同)

Phi*_*hil 10

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

执行命令时:

(add (int 5) (int 2))
Run Code Online (Sandbox Code Playgroud)

会发生什么'执行'add defmulti'从而产生一个列表:

[Integer Integer]
Run Code Online (Sandbox Code Playgroud)

使用生成的,它会查找上面列出的任何"添加defmethod",即:

(add [Integer Integer])
Run Code Online (Sandbox Code Playgroud)

并传递它收到的论据.

定义多方法以便明确传递参数的另一种方法是:

(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))
Run Code Online (Sandbox Code Playgroud)

然后运行以下函数:

(add 12 73)
Run Code Online (Sandbox Code Playgroud)

要么

(add "thank" "you")
Run Code Online (Sandbox Code Playgroud)