Как сделать запись из последовательности значений
У меня есть простое определение записи, например
(defrecord User [name email place])
Каков наилучший способ сделать запись с ее значениями в последовательности
(def my-values ["John" "[email protected]" "Dreamland"])
Я надеялся на что-то вроде
(apply User. my-values)
но это не сработает. Я закончил:
(defn make-user [v]
(User. (nth v 0) (nth v 1) (nth v 2)))
Но я чувствую, что есть лучший способ добиться этого...
Ответы
Ответ 1
Предупреждение: работает только для буквенных последовательностей! (см. комментарий Mihał)
Попробуйте этот макрос:
(defmacro instantiate [klass values]
`(new ~klass [email protected]))
Если вы развернете его с помощью
(macroexpand '(instantiate User ["John" "[email protected]" "Dreamland"]))
вы получите следующее:
(new User "John" "[email protected]" "Dreamland")
который в основном вам нужен.
И вы можете использовать его для создания экземпляров других типов записей или классов Java. В принципе, это просто конструктор класса, который принимает одну последовательность параметров вместо многих параметров.
Ответ 2
функция defrecord создает скомпилированный класс с некоторыми неизменяемыми полями в нем. это не правильные функции clojure (т.е. не класс, реализующий iFn). Если вы хотите называть его конструктором с применением (который ожидает iFun), вам нужно обернуть его анонимной функцией, чтобы применить его, чтобы переварить его.
(apply #(User. %1 %2 %3 %4) my-values)
он ближе к тому, с чего вы начали, хотя ваш подход к определению конструктора с хорошим описательным именем имеет свое собственное обаяние:)
из API:
Note that method bodies are
not closures, the local environment includes only the named fields,
and those fields can be accessed directy.
Ответ 3
Написание собственной функции-конструктора - это, вероятно, путь. Как сказал Артур Ульфельдт, у вас есть функция, которую вы можете использовать как функцию (например, с apply
), а не вызов конструктора Java-interop.
С помощью вашей собственной функции конструктора вы также можете выполнить проверку аргументов или предоставить аргументы по умолчанию. Вы получаете еще один уровень абстракции для работы; вы можете определить make-user
, чтобы вернуть хэш-карту для быстрой разработки, и если позже вы решите изменить записи, вы можете сделать это, не нарушая все. Вы можете писать конструкторы с несколькими атрибутами или принимать ключевые аргументы или делать любое количество других вещей.
(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" "[email protected]" "Somewhere"])
#:user.User{:name "John", :email "[email protected]", :place "Somewhere"}
user> (make-user "John")
#:user.User{:name "John", :email "[email protected]", :place nil}
user> (make-user-keyword-args :place "Somewhere" :name "John")
#:user.User{:name "John", :email "[email protected]", :place "Somewhere"}
user> (make-user-from-hashmap {:user "foo"})
; Evaluation aborted.
; java.lang.Exception: Required argument `name` missing/empty.
Ответ 4
Одна простая вещь, которую вы можете сделать, это использовать деструктурирование.
(defn make-user [[name email place]]
(User. name email place))
Затем вы можете просто называть его следующим образом
(make-user ["John" "[email protected]" "Dreamland"])
Ответ 5
Обновление для Clojure 1.4
defrecord теперь определяет ->User
и map->User
, таким образом следуя в ногах Горана, теперь можно
(defmacro instantiate [rec args] `(apply ~(symbol (str "->" rec)) ~args))
который также работает с нелиберальными последовательностями, как в (instantiate User my-values)
.
В качестве альтернативы, вдоль линий 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)
.