2012-08-14 13 views
19

Sto tentando di creare i miei propri tipi/metodi immutabili con defrecord in Clojure. L'obiettivo è avere un tipo di dati che posso creare istanze di e quindi chiamare i suoi metodi per restituire una nuova copia di se stesso con variabili mutate. Supponiamo che a e b siano vettori. Mi piacerebbe aggiornare un valore in entrambi e restituire una nuova copia dell'intera struttura con quei vettori aggiornati. Questo ovviamente non si compila, sto solo cercando di far passare le mie idee.Defrecord di Clojure - come si usa?

(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 
) 
) 

mi piacerebbe chiamare il numero di volte costruttore e poi il modificatore come vorrei (ci sono altre funzioni che non mutano, ma non voglio per renderlo più complessa per la domanda).

In alternativa, se questo non è Clojure idiomatico, come si dovrebbe fare qualcosa del genere?

risposta

26

Ecco come si definisce il record:

(defrecord MyType [a b]) 

Si noti che in Clojure non è in genere definito "metodi" all'interno del vostro tipo di record in sé (l'eccezione è se si desidera implementare direttamente un'interfaccia Java o un protocollo).

Un costruttore di base (con prefisso ->) ottiene automaticamente generata gratuitamente:

(def foo (->MyType [1 2 3] [4 5 6])) 

foo 
=> #user.MyType{:a [1 2 3], :b [4 5 6]} 

È quindi possibile scrivere funzioni di costruzione più sofisticati che utilizzano questo, ad esempio,

(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]} 

E "mutare-e-ritorno" viene anche gratis - si può semplicemente utilizzare assoc:

(assoc foo :b [7 8 9]) 
=> user.MyType{:a [1 2 3], :b [7 8 9]} 
+0

eccellente, grazie. C'è un modo facile/idiomatico di mutare entrambi i vettori contemporaneamente, prima di tornare? –

+2

o assoc-in: '(assoc-in pippo [: b 0] 12)' – Kevin

+2

Assoc consente di eseguire più chiavi contemporaneamente, ad es. '(assoc foo: a [7 8 9]: b [3 4 5])'. Generalmente, se si desidera eseguire una qualsiasi mutazione più complessa/complessa, è opportuno includerla in una funzione separata (ben denominata). – mikera

2

Clojure defrecord esempio:

;; definire record di indirizzo

(defrecord Address [city state]) 

;, definire il record Persona

(defrecord Person [firstname lastname ^Address address]) 

;; buid costruttore

(defn make-person ([fname lname city state] 
       (->Person fname lname (->Address city state)))) 

;; creare una persona

(def person1 (make-person "John" "Doe" "LA" "CA")) 

;; recuperare i valori

(:firstname person1) 
(:city (:address person1))