Подтвердить что ты не робот

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, как вы должны делать что-то вроде этого?

4b9b3361

Ответ 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))