【问题标题】:How would you idiomatically extend arithmetric functions for other datatypes in Clojure?您将如何在 Clojure 中为其他数据类型惯用地扩展算术函数?
【发布时间】:2013-05-12 00:16:59
【问题描述】:

所以我想使用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))

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

  • 将一个特殊情况编码到您的- 实现中,当您的实现传递一个Number 时,它会传递到clojure.core/-

不幸的是,我不喜欢其中任何一个。第一个可能是最干净的,因为第二个假设您唯一关心的数学运算就是它们的新数据类型和数字。

我是 Clojure 的新手,但我们不应该在这里使用协议或多方法,这样当人们创建/使用自定义类型时,他们就可以“扩展”这些功能,让它们无缝地工作吗? +,- 等是否有理由不支持这个? (或者是吗?从我阅读代码来看,它们似乎不是,但也许我读错了)。

如果我想为其他数据类型编写自己的对常见现有函数(例如 +)的扩展,我应该怎么做才能很好地与现有函数和可能的其他数据类型一起使用?

【问题讨论】:

  • 我也是 Clojure 的新手。为什么不能为此使用协议?
  • 如果您阅读core/+ 的源代码,它不会使用一个,所以没有什么可以挂钩的。不过请查看 mikera 的答案,并按照他的代码进行操作,您将了解如何可以使用协议来做到这一点。

标签: clojure protocols multimethod


【解决方案1】:

它并非完全为此而设计,但您可能会对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 对象。这将为您节省大量实施不同操作的工作。

【讨论】:

  • 嘿,所以core.matrix 实际上是我的第二个示例的来源。那使用协议?我将不得不重新阅读代码,因为我没有注意到!
  • 另外,这并没有真正涉及到原始 - 实现的细节。那么我们是说Clojure 核心实现 硬烘焙并且您不能扩展它吗?如果我试图减去其他两种与向量无关的类型怎么办?我会按照core.matrix 的做法,将我自己的层放在核心之上吗?
  • 所以我重新阅读了你的代码,我看到了它现在是如何工作的:这正是我期望核心 - 工作的方式!我猜它不是因为a)它已经存在了一段时间,b)它比直接暗示的调用慢。我想知道 just 提供支持协议的算术替代方案(即 core.matrix 做什么)的库中是否有价值,因此有一种标准方法可以做到这一点。保持核心- 快速,但允许使用通用但速度较慢的替代方案来替代需要它的人。可能是我过度设计了,我在 Java 领域已经太久了......
  • 我认为您需要清楚您所谈论的“算术替代方案”以及您打算支持的类型。如果它用于类似数组的结构(向量、矩阵、张量等),那么 core.matrix 已经可以满足您的需求。如果是针对一些不同的结构,那么可能会有一个新库的范围,但我想不出太多合理的案例(复数、四元数可能......?)
  • P.S.我认为 core.matrix 会很好地处理特定的 Color 情况,假设您想将其视为 4D RGBA 颜色向量。
【解决方案2】:

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

如果您在自己的命名空间中对自己的数据类型(例如:color/-)有单独的实现,那么由于直接调用该函数,它的性能会更高,而且它也会使事情更加明确和针对特定情况可定制.

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-05-03
      • 1970-01-01
      • 2019-03-22
      • 1970-01-01
      • 1970-01-01
      • 2015-07-09
      • 2014-03-12
      相关资源
      最近更新 更多