Clojure defrecord - как его использовать?
Я пытаюсь создать свой собственный неизменный тип данных/методы с помощью defrecord
в Clojure. Цель состоит в том, чтобы иметь тип данных, который я могу создавать экземпляры, а затем вызывать его методы, чтобы возвращать новую копию себя с мутированными переменными. Скажем, a и b являются векторами. Я хотел бы обновить значение в обоих и вернуть новую копию всей структуры с обновленными векторами. Это, очевидно, не компилируется, я просто пытаюсь передать свои идеи.
(defrecord MyType [a b]
(constructor [N]
; I'd like to build an initial instance, creating a and b as vectors of length N
)
(mutate-and-return []
; I'd like to mutate (assoc the vectors) and return the new structure, a and b modified
)
)
Я хотел бы вызвать конструктор, а затем мутатор столько раз, сколько захочу (есть другие функции, которые не мутируют, но я не хочу усложнять вопрос).
В качестве альтернативы, если это не идиоматический Clojure, как вы должны делать что-то вроде этого?
Ответы
Ответ 1
Здесь вы определяете свою запись:
(defrecord MyType [a b])
Обратите внимание, что в Clojure вы обычно не определяете "методы" внутри самого типа записи (исключение - это если вы хотите напрямую реализовать интерфейс Java или протокол).
Базовый конструктор (с префиксом ->
) автоматически генерируется автоматически:
(def foo (->MyType [1 2 3] [4 5 6]))
foo
=> #user.MyType{:a [1 2 3], :b [4 5 6]}
Затем вы можете написать более сложные функции конструктора, которые используют это, например.
(defn mytype-with-length [n]
(let [a (vec (range n))
b (vec (range n))]
(->MyType a b)))
(mytype-with-length 3)
=> #user.MyType{:a [0 1 2], :b [0 1 2]}
И "mutate-and-return" также предоставляется бесплатно - вы можете просто использовать assoc:
(assoc foo :b [7 8 9])
=> user.MyType{:a [1 2 3], :b [7 8 9]}
Ответ 2
Clojure пример defrecord:
;; определить запись адреса
(defrecord Address [city state])
;; определить запись человека
(defrecord Person [firstname lastname ^Address address])
;; построить конструктор
(defn make-person ([fname lname city state]
(->Person fname lname (->Address city state))))
;; создать человека
(def person1 (make-person "John" "Doe" "LA" "CA"))
;; извлекать значения
(:firstname person1)
(:city (:address person1))