Clojure:支持meta的最小数量实现?

时间:2013-10-13 11:47:04

标签: clojure

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

例如,这个函数:

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

应该

  1. 访问第一个参数的元
  2. 确保它是质量类型,即千克,克等。如果不是,则抛出错误。
  3. 将数值转换为千克。
  4. 为加速做同样的事。
  5. 使用牛顿元返回结果。
  6. 我可以在命名空间中适当地重载*和其他函数。

    唯一的问题是无法将meta附加到Double。 获得一个行为类似于数字但可以拥有元数据的好方法是什么?

2 个答案:

答案 0 :(得分: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-kgto-mss函数自行检查类型,则可以在force中省略它们。不要放弃地图的简单性和透明度,以便在数字上加上元数据的想象方便。

答案 1 :(得分:1)

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

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

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)))