【问题标题】:Clojure: minimal number implementation that supports meta?Clojure:支持元的最小数量实现?
【发布时间】:2013-10-13 11:47:04
【问题描述】:

我想在 Clojure 中实现一些基本的物理/化学公式。 我想强调的不是性能而是便利性,所以主要 功能是类型检查。我在想将元数据附加到数字上 完成任务。

比如这个函数:

(defn force [mass accel]
  (* mass accel))

应该

  1. 访问第一个参数的元
  2. 确保它是质量类型,即千克、克等。如果不是,则抛出错误。
  3. 将数值转换为千克。
  4. 对加速执行相同操作。
  5. 使用牛顿元返回结果。

我可以在我的命名空间中适当地重载* 和其他函数。

唯一的问题是无法将元数据附加到Double。 有什么好方法可以获取行为类似于数字但可以包含元数据的东西?

【问题讨论】:

  • 既然java.lang.Long和朋友都是final,那么使用java.math.BigDecimal是一个选项吗?如果是这样,您可以扩展它并实现clojure.lang.IObj
  • 我应该以哪种方式扩展? deftype/gen-class/reify?可以举个小例子吗?
  • 我刚刚用一些代码添加了答案。我很好奇你将如何实现它,所以它真的不是那么小一个例子:P。希望对您有所帮助。

标签: clojure


【解决方案1】:

如果您只创建包含数字和单位的地图,而不是试图将单位作为数字元数据的一部分偷运进来,这一切都会容易得多。毕竟,该单元在概念上并不是关于数字的簿记数据:它是您正在执行的计算的一个组成部分。并且好像您永远无法在忽略其单位的情况下使用该数字,因此将修饰的数字传递给诸如+ 之类的“愚蠢”单位不感知函数的能力也不有趣。

鉴于这一切,很容易实现您的 force 示例函数:

(defn force [{munit :unit :as mass} {aunit :unit :as accel}]
  (assert (mass? munit))
  (assert (accel? aunit))
  {:unit :newton, :magnitude (* (:magnitude (to-kg mass)) 
                                (:magnitude (to-mss accel)))})

当然,如果您的 to-kgto-mss 函数检查类型本身,您可以在 force 中省略它们。不要放弃地图的简单性和透明性,以在其上拥有带有元数据的数字想象中的便利。

【讨论】:

  • 两次力量仍然是力量。装饰数字意味着我可以在很多普通函数中使用它们。重载的重点是构建一个 DSL,使向函数添加单元检查尽可能简单(最好是(defn force ^:Newton [^:kg mass ^:mss accel] (* mass accel)))。
【解决方案2】:

这是一种使用gen-class 的方法。我只是嘲笑检查和规范化单位的功能。唯一实现的操作是*,它在force中使用。

请注意,由于代码使用gen-classcompile,您需要将以下代码保存到您的leiningen 项目文件夹的src 文件夹中名为big_decimal_meta.clj 的文件中,然后加载它。

BigDecimalMeta 使用gen-class

(ns big-decimal-meta
  (:refer-clojure :exclude [* force])
  (:gen-class
    :name        BigDecimalMeta
    :extends     java.math.BigDecimal
    :state       metadata
    :init        init
    :implements  [clojure.lang.IObj]))

(defn -init [& args]
  [args (atom nil)])

(defn -withMeta [this metadata]
  (reset! (.metadata this) metadata)
  this)

(defn -meta [this]
  (deref (.metadata this)))

(compile 'big-decimal-meta)

*force 函数和一些示例代码:

(def x (with-meta (BigDecimalMeta. 1) {:unit :kg}))
(def y (with-meta (BigDecimalMeta. 3.5) {:unit :mss}))
(def z (with-meta (BigDecimalMeta. 4.5) {:unit :V}))

(defn unit [x]
  (-> x meta :unit))

(defn * [x y]
  (BigDecimalMeta. (str (.multiply x y))))

(defn mass? [x]
  (#{:kg :gr :mg ,,,} (unit x)))

(defn accel? [x]
  (#{:mss ,,,} (unit x)))

(defn to-kg [x] x)
(defn to-mss [x] x)

(defn force [mass accel]
  (assert (mass? mass))
  (assert (accel? accel))
  (let [mass   (to-kg  mass)
        accel  (to-mss accel)]
    (with-meta (* mass accel) {:unit :N})))

(println (force x y) (meta (force x y))) 
(println (force x z) (meta (force x z)))

【讨论】:

    猜你喜欢
    • 2018-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-17
    • 1970-01-01
    • 2010-10-07
    • 2014-07-08
    相关资源
    最近更新 更多