2009-02-16 15 views
6

E 'possibile avere una struttura annidata all'interno di una struttura in Clojure? Si consideri il seguente codice:Struttura del clojure annidata in un'altra struttura

(defstruct rect :height :width) 
(defstruct color-rect :color (struct rect)) 

(defn 
#^{:doc "Echoes the details of the rect passed to it"} 
echo-rect 
[r] 
    (println (:color r)) 
    (println (:height r)) 
    (println (:width r))) 

(def first-rect (struct rect 1 2)) 
;(def c-rect1 (struct color-rect 249 first-rect)) ;form 1 
;output "249 nil nil" 
(def c-rect1 (struct color-rect 249 1 2)) ;form 2 
;output "Too many arguments to struct constructor 

(echo-rect c-rect1) 

Naturalmente questo è un esempio inventato, ma ci sono casi in cui voglio spezzare una grande struttura di dati in strutture più piccole per rendere il codice più facile da mantenere. Come i commenti indicano se faccio il modulo 1 ottengo "249 nil zero", ma se faccio il modulo 2 ottengo "Troppi argomenti per struct constructor".

Se mi sto avvicinando a questo problema nel modo sbagliato, per favore dimmi cosa dovrei fare. La ricerca nel gruppo google Clojure non ha rivelato nulla per me.


Edit:

Credo che non ero più chiaro nella dichiarazione della mia domanda come Pensavo:

1.) E 'possibile annidare una struct all'interno di un altro in Clojure? (Giudicando dal basso si tratta di un sì.)

2.) In tal caso, quale sarebbe la sintassi corretta? (Ancora una volta, a giudicare dal basso sembra che ci siano alcuni modi per farlo.)

3.) Come si recupera un valore con una chiave specificata quando si ha una struttura annidata in un'altra struttura?

Immagino che il mio codice di esempio non mostri davvero quello che stavo cercando di fare molto bene. Sto aggiungendo questo qui in modo che altri alla ricerca di questo possano trovare più facilmente questa domanda e le sue risposte.

risposta

7

sono d'accordo con altri manifesti, in quanto le mappe struct in realtà non supporta l'ereditarietà. Tuttavia, se si vuole fare solo un nuovo struct che utilizza le chiavi di un altro, questo funzionerà:

; Create the rect struct 
(defstruct rect :height :width) 

; Create the color-rect using all the keys from rect, with color added on 
(def color-rect (apply create-struct (cons :color (keys (struct rect))))) 

(defn create-color-rect 
    "A constructor function that takes a color and a rect, or a color height and width" 
    ([c r] (apply struct (concat [color-rect c] (vals r)))) 
    ([c h w] (struct color-rect c h w))) 

non avete bisogno della funzione echo-rect, si può semplicemente di valutare l'istanza struct mappa per vedere cosa c'è in iT:

user=> (def first-rect (struct rect 1 2)) 
#'user/first-rect 
user=> first-rect 
{:height 1, :width 2} 
user=> (create-color-rect 249 first-rect) 
{:color 249, :height 1, :width 2} 
user=> (create-color-rect 249 1 2) 
{:color 249, :height 1, :width 2} 
+0

Grazie Paul, questo è esattamente ciò che volevo sapere. –

1

Sono davvero nuovo nel clojure, quindi potrei sbagliarmi. Ma credo che, non si può fare qualcosa di simile

(defstruct color-rect :color (struct rect)) 

Per quanto ho capito Clojure-struct, questo creerebbe una struct (in pratica una mappa con le chiavi noti), che ha in qualche modo la struct 'rect' come una delle sue chiavi.

La mia ipotesi è sostenuta dall'osservazione che una semplice valutazione di (struct rect) produce

{:height nil, :width nil} 

considerando che una valutazione di (struct colore-rect) rendimenti:

{:color nil, {:height nil, :width nil} nil} 

EDIT: Che cosa potrebbe aiutarti è il fatto, che le strutture non sono limitate alle chiavi, sono definite con. Sembra come se si potesse realizzare, quello che si sta cercando da qualcosa di simile a questo:

(def c-rect1 (struct-map color-rect :color 249 :height 1 :width 1)) ;form 3 
+0

Questo è corretto: Si sta utilizzando una struct rect vuota come la chiave per una dei valori di color-rect. Avrebbe più senso usare qualcosa come: rect. –

6

strutture annidamento è possibile e talvolta desiderabile. Tuttavia, sembra che tu stia cercando di fare qualcosa di diverso: sembra che tu stia cercando di usare l'ereditarietà dei tipi di struttura piuttosto che la composizione. Cioè, nel modulo 2 si sta creando un rect a colori che contiene un rect ma si sta tentando di costruire un'istanza come se fosse un rect. Il modulo 1 funziona perché stai costruendo c-rect1 da un rect preesistente, che è il modo corretto di usare la composizione.

Una ricerca rapida sul gruppo Clojure o solo sul web in generale dovrebbe portare a una buona descrizione della distinzione tra composizione ed eredità. In Clojure, la composizione o la dattilografia (vedi di nuovo Google) è quasi sempre preferita all'ereditarietà.


Edit:

In risposta alla tua domanda # 3: Un'alternativa all'utilizzo -> per l'estrazione dei dati in strutture nidificate, come Brian Carper descritto nella sua risposta, è get-in, insieme con la sua fratelli assoc-in e update-in:

Ad esempio:

(def cr {:rect {:height 1, :width 2}, :color :blue}) 
(get-in cr [:rect :width]) 
;; => 2 

(assoc-in cr [:rect :height] 7) 
;; => {:rect {:height 7, :width 2}, :color :blue} 

(update-in cr [:rect :width] * 2) 
;; => {:rect {:height 1, :width 4}, :color :blue} 

(assoc-in cr [:a :new :deeply :nested :field] 123) 
;; => {:a {:new {:deeply {:nested {:field 123}}}}, 
;;  :rect {:height 1, :width 2}, :color :blue} 
6

è possibile effettuare una struct essere un valore di un'altra struct se si dà una chiave per essere associate a. Potresti farlo come di seguito.

(è possibile accedere facilmente il coraggio di arbitrariamente annidate hash/struct via ->, come un po 'di zucchero sintassi.)

(defstruct rect :height :width) 
(defstruct color-rect :rect :color) 

(def cr (struct color-rect (struct rect 1 2) :blue)) 
;; => {:rect {:height 1, :width 2}, :color :blue} 

(:color cr)   ;; => :blue 
(:width (:rect cr)) ;; => 2 
(-> cr :color)  ;; => :blue 
(-> cr :rect :width) ;; => 2 
1

Mi rendo conto che questa è una vecchia questione ora, ma mi si avvicinò con la macro seguente:

(defmacro extendstruct [n b & k] 
    `(def ~n 
    (apply create-struct 
     (clojure.set/union 
     (keys (struct ~b)) 
     #{[email protected]})))) 

che permetterebbe di scrivere questo:

(defstruct rect :width :height) 
(extendstruct color-rect rect :color) 

Testing:

(struct rect)  ; {:width nil, :height nil} 
(struct color-rect) ; {:color nil, :width nil, :height nil} 

Questo sarebbe quello che volevi?

Potrebbe anche essere modificato in modo da poter utilizzare una raccolta di strutture. O anche consentono di utilizzare altre definizioni struct come nomi di chiavi, che sono espansi automaticamente nelle chiavi prodotte da un tale struct:

(defstructx one :a :b) 
(defstructx two :c one :d) 
(defstructx three :e two :f :g) 
; three 
(keys (struct three)) ; #{:e :c :a :b :d :f :g}