能够接受原始数据和元数据的最简单的Clojure对象是什么?

7
我想在Clojure中为字节数组添加元数据,由于不允许这样做,所以我想尝试的一个选项是最简单的可行的对象包装器。
下面是“with-meta”的源代码,您可以访问此处
这使我开始查看Clojure.lang.IObj,但我还没有找到我想要的内容。

你看过clojure.lang.Obj了吗? - DaoWen
@DaoWen 是的,我有。我正在建立一个例子;如果有任何希望,我会分享的。 - David J.
2个回答

7

以下是如何创建一个支持元数据的deftype

(import '(java.io Writer))

(deftype Box [value _meta]
  clojure.lang.IObj
  (meta [_] _meta)
  (withMeta [_ m] (Box. value m))
  clojure.lang.IDeref
  (deref [_] value)
  Object
  (toString [this]
    (str (.getName (class this))
         ": "
         (pr-str value))))

(defmethod print-method Box [o, ^Writer w]
  (.write w "#<")
  (.write w (.getName (class o)))
  (.write w ": ")
  (.write w (-> o deref pr-str))
  (.write w ">"))

(defn box
  ([value] (box value nil))
  ([value meta] (Box. value meta)))

以下是一些使用示例:

user> (def boxed (box (->> (range 5)
                        (map byte)
                        (byte-array))
                      {:stuff :foo}))
#<Var@1acd39b: #<Box@c50aa1: #>>
user> @boxed
[0, 1, 2, 3, 4]
user> (meta boxed)
{:stuff :foo}
user> (meta (with-meta boxed {:stuff :bar}))
{:stuff :bar}

这是我能想到的最简单的方法,可以在字节数组上放置元数据(reify不能与clojure.lang.IObj一起使用,而记录包括更多无关功能)。
另一个选项(根据上下文可能更简单)是将字节数组存储在地图中,元数据可以直接放在旁边或作为实际元数据。

是的,deftype 比手写 Java 类更加简洁。还有一点需要注意,Clojure 会将空元数据存储为 nil,而不是 {}。另外,实现 toString 方法也是很不错的选择。 - David J.
@DavidJames,我添加了简单的toStringprint-method实现,并将默认元数据从{}更改为nil - jbm

2

在 #clojure IRC 上与一些人讨论后,我编写了一个简单的 Java 类 MetaBox,它实现了 clojure.lang.IObj 接口。你可以在 Clojure 中使用 metabox/boxmetabox/val,以及通常的元数据函数,比如 metawith-meta

; [metabox "0.1.0"]
(require '[metabox.core :refer (box val)])
(def critical-density (box 0.692 {:uncertainty 0.01}))
(val critical-density) ; 0.692
(meta critical-density) ; {:uncertainty 0.01}

你可以在 clj-metabox 找到源代码和自述文件。

更新: 感谢一些讨论和建议(见下文),从版本0.2.0开始,API使用deref替代val:

; [metabox "0.2.0"]
(require '[metabox.core :refer (box)])
(def critical-density (box 0.692 {:uncertainty 0.01}))
@critical-density ; 0.692
(meta critical-density) ; {:uncertainty 0.01}

选择这个而不是 deftype 的考虑是什么?顺便说一下,由于没有前缀 val 会遮盖 clojure.core/val,因此使用其他名称(例如 valueval-of)可能更值得一试。 - jbm
2
实际上,clojure.lang.IDeref 可能是公开值的好方法。我更新了我的示例代码以包括它(并采用了术语 box)。 - jbm
@jbm:我上面关于引用类型的评论是关于clojure.lang.IDeref的。 - David J.
嗯,ReducedBox 一样似乎并不更像引用,但我同意取消引用会应用一个引用的观点。(我开始考虑 IDeref 的原因是,将值装箱会添加一个 大致 类似于 Var 的间接级别)。在通俗的英语中,Box 是一个值和其元数据的引用,尽管它没有 Clojure 引用类型的状态/身份特征。(现在我要停止对您的问题进行骚扰并去睡觉 :)) - jbm
1
“引用类型”是什么意思?当然,实现IDeref使得某种定义下的类型成为“引用类型”,但仅仅使用不同的解引用函数并不能使类型变得不那么像“引用类型”。 - Nathan Davis
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接