0

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

例如,这个函数:

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

应该

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

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

唯一的问题是不可能将 meta 附加到Double. 什么是获得行为像数字但可以包含元数据的东西的好方法?

4

2 回答 2

2

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

鉴于这一切,很容易实现您的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-kgandto-mss函数检查类型本身,您可以在force. 不要放弃地图的简单性和透明性,以在其上拥有带有元数据的数字的想象便利。

于 2013-10-14T00:28:01.303 回答
1

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

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

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)))
于 2013-10-13T14:17:10.983 回答