Clojure 如何从一系列值中创建记录

Clojure 如何从一系列值中创建记录,clojure,Clojure,例如,我有一个简单的记录定义 (defrecord User [name email place]) 使记录的值按顺序排列的最佳方法是什么 (def my-values ["John" "john@example.com" "Dreamland"]) 我希望像这样的事情 (apply User. my-values) 但那是行不通的。我最后做了: (defn make-user [v] (User. (nth v 0) (nth v 1) (nth v 2))) 但是我感觉有更好的方

例如,我有一个简单的记录定义

(defrecord User [name email place])
使记录的值按顺序排列的最佳方法是什么

(def my-values ["John" "john@example.com" "Dreamland"])
我希望像这样的事情

(apply User. my-values)
但那是行不通的。我最后做了:

(defn make-user [v]
  (User. (nth v 0) (nth v 1) (nth v 2)))

但是我感觉有更好的方法来实现这一点…

defrecord函数创建一个编译类,其中包含一些不可变的字段。它不是一个合适的clojure函数(即:不是一个实现iFn的类)。如果你想用apply调用它的构造函数(它需要一个iFun),你需要把它包装在一个匿名函数中,这样apply就能够消化它

(应用(用户%1%2%3%4)我的值)

虽然您用一个好的描述性名称定义构造函数的方法有它自己的魅力,但它更接近您开始使用的方法:)

从:

请注意,方法体是 不是闭包,本地环境仅包括命名字段,
这些字段可以直接访问。警告:仅适用于文字序列(见米哈什的评论)

请尝试以下宏:

(defmacro instantiate [klass values] 
        `(new ~klass ~@values))
如果使用以下方法展开:

(宏扩展)(实例化用户[“John”john@example.com“梦境”])

您将得到以下结果:

(新用户“John”john@example.com“梦境”)

这基本上就是你需要的


您可以使用它来实例化其他记录类型或Java类。基本上,这只是一个类构造函数,它只接受一个参数序列而不是多个参数。

编写自己的构造函数可能是一种方法。正如Arthur Ulfeldt所说,您可以使用一个函数作为函数(例如,使用
apply
),而不是Java互操作构造函数调用

使用自己的构造函数,还可以进行参数验证或提供默认参数。你获得了另一个抽象层次的工作;您可以定义
makeuser
以返回一个哈希映射以进行快速开发,如果您以后决定更改为记录,则可以在不破坏所有内容的情况下执行此操作。您可以编写具有多个arity的构造函数,或者使用关键字参数,或者执行任意数量的其他操作

(defn- default-user [name]
  (str (.toLowerCase name) "@example.com"))

(defn make-user
  ([name] (make-user name nil nil))
  ([name place] (make-user name nil place))
  ([name user place]
     (when-not name
       (throw (Exception. "Required argument `name` missing/empty.")))
     (let [user (or user (default-user name))]
       (User. name user place))))

(defn make-user-keyword-args [& {:keys [name user place]}]
  (make-user name user place))

(defn make-user-from-hashmap [args]
  (apply make-user (map args [:name :user :place])))

user> (apply make-user ["John" "john@example.com" "Somewhere"])
#:user.User{:name "John", :email "john@example.com", :place "Somewhere"}

user> (make-user "John")
#:user.User{:name "John", :email "john@example.com", :place nil}

user> (make-user-keyword-args :place "Somewhere" :name "John")
#:user.User{:name "John", :email "john@example.com", :place "Somewhere"}

user> (make-user-from-hashmap {:user "foo"})
; Evaluation aborted.
; java.lang.Exception: Required argument `name` missing/empty.

您可以做的一件简单的事情就是利用分解

(defn make-user [[name email place]]
  (User. name email place))
那么你可以这样称呼它

(make-user ["John" "John@example.com" "Dreamland"])

Clojure 1.4的更新

defrecord现在定义了
->User
map->User
,因此现在可以跟随Goran的足迹

(defmacro instantiate [rec args] `(apply ~(symbol (str "->" rec)) ~args))
这也适用于非文字序列,如
(实例化用户我的值)
。 或者,按照
map->User
的思路,可以定义一个函数
seq->User

(defmacro def-seq-> [rec] `(defn ~(symbol (str "seq->" rec)) [arg#] (apply ~(symbol (str "->" rec)) arg#)))

(def-seq-> User)

这将允许
(seq->User my values)

肯定比我的第N-y件事更喜欢这个。。但我还是要记下所有预期的论点。。我正在寻找一些可以应用于任何记录定义的通用方法。。。如果有一个…并补充答案:1。看一看,提供一些额外的东西。2.我认为他们计划在将来的clojure版本中使用类似的东西。顺便说一句:您可以在构造函数中使用解构绑定。只是为了表面的原因。(defn make user[[nep]](user.nep)).NB。这仅在
实例化
宏接收到一个值的文本序列表时才起作用。例如,
(定义VAL[“John”john@example.com“Dreamland”])
后跟
(实例化用户VAL)
将不起作用。另外,宏不能与
apply
@Mihał一起使用:谢谢。我没想到那个角落的案子。幸运的是,在使用literal的情况下,不需要apply。我并不需要它,但我发现了一种讨厌的方法,它也适用于非literal seqable-
(defn实例化[klass值](eval(read string(str)(new)(.getName klass)”(let[s(str值)](subs s1(-(.length s)1))))
您可以简单地
(应用->用户值)
->User
是正常功能。
(defmacro def-seq-> [rec] `(defn ~(symbol (str "seq->" rec)) [arg#] (apply ~(symbol (str "->" rec)) arg#)))

(def-seq-> User)