您如何在Clojure中以其他方式扩展其他数据类型的算术函数?

时间:2013-05-12 00:16:59

标签: clojure protocols multimethod

所以我想用java.awt.Color来表达某些东西,我希望能够编写这样的代码:

(use 'java.awt.Color)
(= Color/BLUE (- Color/WHITE Color/RED Color/GREEN))

查看-的核心实现,它具体讨论clojure.lang.Numbers,这对我来说意味着我没有做任何事情来“挂钩”到核心实现并扩展它。

在互联网上四处看看,人们似乎有两件不同的事情:

  • 编写自己的defn -函数,它只知道他们感兴趣的数据类型。要使用你可能最终为命名空间加前缀,所以类似:

    (= Color/BLUE (scdf.color/- Color/WHITE Color/RED Color/GREEN))

    或者use命名空间,并在需要数字数学时使用clojure.core/-

  • 在您的实施传递-时,clojure.core/-实施内容中的特殊情况进行编码,然后传递到Number

不幸的是,我不喜欢其中任何一个。第一个可能是最干净的,因为第二个假设你关心做数学的唯一事情是他们的新数据类型和数字。

我是Clojure的新手,但我们不应该在这里使用Protocols或Multimethods,这样当人们创建/使用自定义类型时,他们可以“扩展”这些功能,使它们无缝地工作?有+-等不支持这种情况的原因吗? (或者他们?他们似乎没有阅读我的代码,但也许我读错了。)

如果我想将自己的扩展编写到常见的现有函数(如+)以获取其他数据类型,我该怎么做才能使它与现有函数和其他可能的数据类型完美匹配?

3 个答案:

答案 0 :(得分:5)

这不是为此而设计的,但core.matrix可能会引起您的兴趣,原因如下:

  • 源代码提供了如何使用协议定义与各种不同类型一起使用的操作的示例。例如,(+ [1 2] [3 4]) => [4 6])。值得研究如何完成:基本上,运算符是调用协议的常规函数​​,每种数据类型都通过extend-protocol
  • 提供协议的实现。
  • 您可能有兴趣将java.awt.Color作为core.matrix实现(即作为4D RGBA向量)。我在这里用BufferedImage做了类似的事情:https://github.com/clojure-numerics/image-matrix。如果您实现了基本的core.matrix协议,那么您将获得整个core.matrix API以使用Color个对象。这将为您节省大量实施不同操作的工作。

答案 1 :(得分:4)

不基于协议在核心中进行算术运算的可能原因(并且仅使它们成为数字的工作)是性能。协议实现需要额外的查找以选择所需功能的正确实现。虽然从设计的角度来看,基于协议的实现并在需要时扩展它们可能会感觉很好,但是当你有一个紧密的循环来执行这些操作很多次(这是非常常见的算术运算用例)你会开始感觉由于在运行时发生的每个操作的附加查找,性能问题。

如果您在自己的命名空间中对自己的数据类型(例如:color/-)有单独的实现,那么由于直接调用该函数它会更加高效,并且它还可以使事情更加明确和可自定义具体案例。

这些函数的另一个问题是它们的可变性(即它们可以采用任意数量的参数)。这是提供协议实现的一个严重问题,因为协议扩展类型检查仅适用于第一个参数。

答案 2 :(得分:2)

您可以查看algo.generic中的algo.generic.arithmetic。它使用多方法。