Clojure: назначение полей для отложенной записи с карты - PullRequest
10 голосов
/ 23 декабря 2010

После Как сделать запись из последовательности значений , как написать вызов конструктора defrecord и назначить поля из Map, оставив неназванные поля nil?

(defrecord MyRecord [f1 f2 f3])
(assign-from-map MyRecord {:f1 "Huey" :f2 "Dewey"})  ; returns a new MyRecord

Я предполагаю, что для этого можно написать макрос.

Ответы [ 3 ]

12 голосов
/ 23 декабря 2010

Вы можете просто merge отобразить карту в записи, инициализированной с помощью nil s:

(merge (MyRecord. nil nil nil) {:f1 "Huey" :f2 "Dewey"})

Обратите внимание, что записи могут содержать значения, хранящиеся под дополнительными ключами, в стиле карты.

Список полей записи можно получить с помощью отражения:

(defn static? [field]
  (java.lang.reflect.Modifier/isStatic
   (.getModifiers field)))

(defn get-record-field-names [record]
  (->> record
       .getDeclaredFields
       (remove static?)
       (map #(.getName %))
       (remove #{"__meta" "__extmap"})))

Последняя функция возвращает последовательность строк:

user> (get-record-field-names MyRecord)
("f1" "f2" "f3")

__meta и __extmap поля, используемые записями Clojure для хранения метаданных и для поддержки функциональности карты, соответственно.

Вы можете написать что-то вроде

(defmacro empty-record [record]
  (let [klass (Class/forName (name record))
        field-count (count (get-record-field-names klass))]
    `(new ~klass ~@(repeat field-count nil))))

и использовать его для создания пустых экземпляров классов записейвот так:

user> (empty-record user.MyRecord)
#:user.MyRecord{:f1 nil, :f2 nil, :f3 nil}

Полное имя здесь важно.Он будет работать до тех пор, пока класс записи будет объявлен ко времени компиляции любых empty-record форм, ссылающихся на него.

Если empty-record записано как функция, можно ожидать, что он ожидаетфактический класс в качестве аргумента (избегая «полностью квалифицированной» проблемы - вы можете назвать свой класс любым удобным для вас способом в данном контексте), но ценой выполнения отражения во время выполнения.

4 голосов
/ 08 ноября 2015

Clojure генерирует в эти дни функцию map-> RecordType , когда определена запись.

(defrecord Person [first-name last-name])
(def p1 (map->Person {:first-name "Rich" :last-name "Hickey"}))

Карта не обязательна для определения всех полей в определении записи, и в этом случае отсутствующие ключи имеют нулевое значение в результате. Карта также может содержать дополнительные поля, которые не являются частью определения записи.

1 голос
/ 23 декабря 2010

Как упоминалось в ответах на связанный вопрос, код здесь показывает, как создать макрос defrecord2 для генерации функции конструктора, которая принимает карту, как показано здесь . Особый интерес представляет макрос make-record-constructor .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...