Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 创建与其他记录类型相同的记录_Clojure_Clojurescript - Fatal编程技术网

Clojure 创建与其他记录类型相同的记录

Clojure 创建与其他记录类型相同的记录,clojure,clojurescript,Clojure,Clojurescript,我有一个例子,我想根据记录实例的类型创建一个新的记录实例,该记录实例作为参数与属性映射一起出现 (defn record-from-instance [other attrs] ;; Code that creates the new record based on "other" ) 我现在看到的是以下几行: (defn record-from-instance [other attrs] (let [matched (s/split (subs (str (class

我有一个例子,我想根据记录实例的类型创建一个新的记录实例,该记录实例作为参数与属性映射一起出现

(defn record-from-instance
  [other attrs]
  ;; Code that creates the new record based on "other"
  )
我现在看到的是以下几行:

(defn record-from-instance
  [other attrs]
  (let [matched (s/split (subs (str (class other)) 6) #"\.")
        path (s/join "." (pop matched))
        class-name (peek matched)]
    ((resolve (symbol (str path "/" "map->" class-name))) attrs)))
有没有其他我看不到的更简单、更惯用的方法

谢谢

编辑

为了提供更多细节,我正在构建一个AST,其中节点是记录,我正在使用拉链访问并可能更改/删除AST的部分。我有一个
IZipableTreeNode
协议

(defprotocol IZipableTreeNode
  (branch? [node])
  (children [node])
  (make-node [node children]))
实现
IZipableTreeNode
的不同类型之间是
IPersistentMap

  IPersistentMap
  (branch? [node] true)
  (children [node] (seq node))
  (make-node [node children]
    (let [hmap (into {} (filter #(= (count %) 2)) children)]
      (if (record? node)
        (record/from-instance node hmap)
         hmap)))

当访问者说从节点删除(或更改)字段时,
make节点
被调用,
node
作为记录AST节点和
children
新的键/值对(可能不包含
节点
中的某些字段)

我以为
clojure.core/empty
是用来做这件事的。就是我想,

(defrecord Foo [x]) 
(empty (Foo. 1))
会回来吗

#user.Foo{:x nil}
但现在肯定不是这样:我不确定这是改变了还是我记错了。我找不到一个超级干净的方法来做到这一点,但我至少有比你的方法更好的方法。您正在使用的
user/map->Foo
函数基于与类
user.Foo/create
一起生成的静态方法,通过反射直接调用该方法更为经典

user> ((fn [r attrs]
         (.invoke (.getMethod (class r) "create" 
                              (into-array [clojure.lang.IPersistentMap]))
                  nil, (into-array Object [attrs])))
       (Foo. 1) {:x 5})
#user.Foo{:x 5}
然而,现在我突然想到,你可能不需要做这些!你一开始就有一种先入为主的观念,即实现“基于以前的东西构建新东西”的目标的方法是从头开始,但为什么要这样做呢?只要传递到函数中的记录没有添加任何“扩展”字段(即,不属于记录定义本身的字段),那么您只需使用
clojure.core/into

(into (Foo. 1) {:x 5}) ;=> #user.Foo{:x 5}

您也可以这样做:

(defn clear [record]
  (reduce (fn [record k]
            (let [without (dissoc record k)]
              (if (= (type record) (type without))
                without
                (assoc record k nil))))
          record
          (keys record)))

(defn map->record [record m]
  (into (clear record) m))
例如:

(defrecord Foo [x y])

(map->record (map->Foo {:x 1 :y 2 :z 3}) {:y 4})
;;=> #example.core.Foo{:x nil, :y 4} 

我不确定这是比@amalloy的反射方法更有效还是效率更低。

谢谢@amalloy的回答!我在中看到了
,但这有一个问题。我希望新的记录实例具有与映射完全相同的属性,但是
into
将映射与记录实例合并。例如,使用
(defrecord Bar[xy])
(into(Bar.1){:y3})
将返回
#user.Bar{:x1:y3}
,而不是
#user.Bar{:xnil:y3}
。我的意思是,您所写的内容也不完全相同
{:xnil:y3}
{:y3}
是完全不同的映射。在最一般的情况下,您所要求的是不可能的,因为映射可以有任意数量的字段(包括零),而记录可以有非零数量的必填字段。你必须决定你最喜欢哪种折衷办法。@amalloy它看起来像,所以我认为这是一个错误。@amalloy你是对的,地图不完全一样。但是
map->
构造函数假定所有字段都是可选的,这就是为什么要首先使用map调用。考虑到字段值的要求仅存在于位置构造函数中,并且我正在编写的代码假设映射已被传递,我可以接受这种折衷办法。@SamEstep我认为ClojureScript的记录
为空。它有
IEmptyableCollection
,这是调用
empty
的接口。方法不错。如果
clear
函数仅用于在记录上调用,那么您可以安全地放弃reducer中的类型相等检查,因为在记录上调用
dissoc
总是返回一个映射。但是,对于我正在编写的代码,我不希望像您的示例那样使用
nil
值字段。它应该是不存在的,而不是
nil
@g7s,我不明白<记录上的code>dissoc
仅当被
dissoc
ed的键是记录中所需的键之一时才返回映射;这正是我使用
type
的原因。在你关于amalloy的回答中,你明确地说,他的
进入
方法的问题在于它没有返回一条记录,该记录的条目为
:x nil
。你能澄清一下你在这里的评论是什么意思吗?我错了
dissoc
如果密钥在所需的密钥中,确实会返回一个映射。不要理会我对这件事的评论。关于我对amalloy回答的评论,我的意思是
#user.Bar{:y 3}
(没有
x
字段),但我意识到我晚了一点犯了一个错误,无法编辑我的评论:/@g7s你也错了;给定
(defrecord Bar[xy])
,则不存在
#user.Bar{:y 3}
这样的对象。你能得到的最接近的是
#user.Bar{:x nil:y 3}
。好了,现在已经很晚了,我想我需要一些睡眠:P是的,正确的是
#user.Bar{:x nil:y 3}
,你的函数给出了相同的结果。美好的真的,整个问题和你对它的所有评论让我不禁要问,“你为什么要使用记录?”听起来你希望记录的行为完全像地图一样,你要做所有这些工作来规避它们的核心功能。@amalloy是的,我使用记录作为AST的节点。你认为我最好使用带有类型字段的普通映射吗?在description@g7s我发现你编辑的例子非常混乱。在协议中使用记录的全部要点是使用Clojure的多态性特性,但这里您只是为映射实现协议(所有向量都是映射),然后显式地检查它是否是记录,并对所有记录类型使用相同的代码。您能演示一下为什么要使用记录吗?@SamEstep因为AST节点类型很多,所以对每种类型都实现
IZipableTreeNode
非常困难