gpt4 book ai didi

Clojure:如何在函数内创建记录?

转载 作者:行者123 更新时间:2023-12-02 06:06:15 25 4
gpt4 key购买 nike

在 clojure 中,我想在函数内创建一条记录。

我试过:

(defn foo []
(defrecord MyRecord [a b])
(let [b (MyRecord. "1" "2")]))

但它会引发异常:

java.lang.IllegalArgumentException: Unable to resolve classname: MyRecord

有什么想法吗?

最佳答案

重点

您应该只在顶层使用defrecord1

因此,如果您确实需要自定义记录类型,您应该在 foo 之外定义它(在您的代码中的某个点,它在 foo 的定义之前得到处理).

否则,您可以只使用常规 map 。特别是,如果 foo 将创建多个“类型”的实体(在概念层面),尝试为每个类型创建一个记录类型(Java 类)可能没有意义;自然的解决方案是使用包含 :type 键的映射来指示所表示的实体的种类。

为什么不起作用

问题中的代码无法编译,因为 Clojure 的编译器在编译时将作为第一个参数提到的类名解析为 new 形式。 ((MyRecord. "1""2")在宏展开过程中展开为(new MyRecord "1""2")。)这里的名字MyRecord 还不能解析为合适的类,因为后者还没有被定义(它将在 foo 之后由 defrecord 形式创建称为)。

为了解决这个问题,你可以做一些可怕的事情,比如

(defn foo []
(eval '(defrecord MyRecord [x y]))
(let [b (clojure.lang.Reflector/invokeConstructor
;; assuming MyRecord will get created in the user ns:
(Class/forName "user.MyRecord")
(into-array Object ["1" "2"]))]
b))

这会导致小猫通过反射调用其 finalize 方法,从而导致可怕的死亡。

作为最后的评论,上面那个可怕的版本可以工作,但它也会在每次调用时以相同的名称创建一个新的记录类型。这导致随之而来的怪异。尝试以下示例以获得 flavor :

(defprotocol PFoo (-foo [this]))

(defrecord Foo [x]
PFoo
(-foo [this] :foo))

(def f1 (Foo. 1))

(defrecord Foo [x])

(extend-protocol PFoo
Foo
(-foo [this] :bar))

(def f2 (Foo. 2))

(defrecord Foo [x])

(def f3 (Foo. 3))

(-foo f1)
; => :foo
(-foo f2)
; => :bar
(-foo f3)
; breaks

;; and of course
(identical? (class f1) (class f2))
; => false
(instance? (class f1) f2)
; => false

此时,(Class/forName "user.Foo")(再次假设所有这一切都发生在user命名空间中)返回的类f3,其中f1f2都不是实例。


1 宏偶尔可能会输出包含在 do 中的 defrecord 形式以及一些其他形式;不过,顶层 do 是特殊的,因为它们表现得好像它们包装的表单是在顶层单独处理的。

关于Clojure:如何在函数内创建记录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9308138/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com