2009-02-16 16 views
6

¿Es posible tener una estructura anidada dentro de una estructura en Clojure? Considere el siguiente código:Estructura de Clojure anidada en otra estructura

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

Por supuesto, esto es un ejemplo artificial, pero hay casos en los que quiero romper una estructura de datos de gran tamaño en subestructuras más pequeños para hacer el código más fácil de mantener. Como los comentarios indican que si hago el formulario 1, obtengo "249 nil nil", pero si hago el formulario 2, obtengo "Demasiados argumentos para struct constructor".

Si me estoy acercando a este problema de la manera incorrecta, por favor dígame qué debo hacer. Buscar en el grupo de Google Clojure no me mostró nada.


Editar:

supongo que no era tan clara en el estado de mi pregunta que yo:

1.) ¿Es posible anidar una estructura dentro de otra en Clojure? (A juzgar por abajo eso es un sí.)

2.) De ser así, ¿cuál sería la sintaxis correcta? (De nuevo, a juzgar por abajo, parece que hay algunas maneras en que se puede hacer esto.)

3.) ¿Cómo se obtiene un valor con una clave específica cuando se tiene una estructura anidada dentro de otra estructura?

Supongo que mi código de muestra realmente no demostró lo que estaba tratando de hacer muy bien. Estoy agregando esto aquí para que otros que busquen esto puedan encontrar esta pregunta y sus respuestas más fácilmente.

Respuesta

7

Estoy de acuerdo con otras críticas en que los mapas de struct en realidad no soporta herencia. Sin embargo, si sólo quiere hacer una nueva estructura que utiliza las teclas de otra, esto va a funcionar:

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

que no es necesario la función echo-rect, sólo tiene que evaluar la instancia de correlación estructura para ver lo que hay en 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

Gracias Paul - esto es más o menos exactamente lo que quería saber. –

1

Soy realmente nuevo en clojure, así que podría estar equivocado. Pero creo que, no se puede hacer algo como

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

Por lo que yo entiendo clojure-estructuras, esto crearía una estructura (básicamente un mapa con claves conocidas), que tiene de alguna manera la estructura 'rect' como una de sus llaves.

Mi hipótesis está respaldada por la observación de que una evaluación sencilla de (rect struct) produce

{:height nil, :width nil} 

Mientras que una evaluación de (estructura de color-Rect) se obtiene:

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

EDIT: Lo podría ayudarlo es el hecho de que las estructuras no están limitadas a las teclas, se definen con ellas. Parece como si se pudiera lograr, lo que está tratando de algo como esto:

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

Esto es correcto: está usando una rect struct vacía como la clave para una de los valores de color-rect. Tendría más sentido usar algo como: rect. –

6

estructuras de anidamiento es posible y deseable algunas veces. Sin embargo, parece que intentas hacer algo diferente: parece que intentas usar la herencia de los tipos de estructura en lugar de la composición. Es decir, en el formulario 2 está creando un color-rect que contiene un rect pero está intentando construir una instancia como si fuera a rect. La Forma 1 funciona porque estás construyendo c-rect1 a partir de un rect preexistente, que es la forma correcta de usar la composición.

Una búsqueda rápida en el grupo Clojure o simplemente en la web en general debería llevarlo a una buena descripción de la distinción entre composición y herencia. En Clojure, casi siempre se prefiere la composición o la tipificación de patos (ver Google de nuevo) a la herencia.


Editar:

En respuesta a su pregunta # 3: Una alternativa al uso -> para la extracción de datos de estructuras anidadas, como Brian Carper describe en su respuesta, es conseguir-en, junto con su hermanos Assoc de entrada y actualización de entrada:

Por ejemplo:

(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

Puede hacer que una estructura sea un valor de otra estructura si le da una clave para asociarse. Podrías hacerlo como a continuación.

(se puede acceder fácilmente a los intestinos de manera arbitraria anidados hashes/estructuras a través de ->, como un poco de azúcar en la sintaxis.)

(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

Comprendo que esto es una vieja pregunta ahora, pero se le ocurrió la macro siguiente:

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

lo que permitiría a escribir esto:

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

Probando:

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

¿Sería esto lo que querías?

También podría modificarse para que se pueda utilizar una colección de estructuras. O incluso le permiten usar otras definiciones de estructuras como nombres de teclas, que se expanden automáticamente en las claves producidas por dicha estructura:

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