Clojure Structure nested in another structure

Is it possible to have a structure nested in a structure in Clojure? Consider the following code:

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

Of course, this is a contrived example, but there are cases when I want to break a large data structure into smaller substructures in order to simplify the work with the code. Since the comments indicate that if I am making form 1, I get "249 zero zero", but if I am making form 2, I get "Too many arguments for constructor constructor".

If I approach this problem in a different way, tell me what I should do. A search in the Clojure google group showed nothing for me.


Edit:

I think I was not so clear in the statement of my question, as I thought I:

1.) Is it possible to decompose one structure inside another in Clojure? (Judging by the fact that yes.)

2.) If so, what will be the correct syntax? (Again, judging by the bottom, it looks like there are several ways to do this.)

3.) How do you get the value for the specified key when you have a structure nested in another structure?

I think that my sample code did not actually demonstrate what I was trying to do very well. I am adding this here so that others looking for it can more easily find this question and its answers.

+6
struct clojure nested
source share
5 answers

I agree with other posters that structural maps do not really support inheritance. However, if you just want to create a new structure that uses the keys of another, this will work:

 ; 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" ([cr] (apply struct (concat [color-rect c] (vals r)))) ([chw] (struct color-rect chw))) 

You do not need the echo-rect function, you can just evaluate an instance of the map structure to see what is 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} 
+7
source share

Nested structures are possible and sometimes desirable. However, it looks like you are trying to do something else: it looks like you are trying to use structure type inheritance rather than composition. That is, in form 2, you create a color-rect that contains rect, but you are trying to build an instance as if it were direct. Form 1 works because you create c-rect1 from a pre-existing rectangle, which is the right way to use composition.

A quick search in the Clojure group or just the Internet as a whole should lead you to a good description of the differences between composition and inheritance. In Clojure, a compound or duck seal (see Google again) is almost always preferable to inheritance.


Edit:

In answer to your question No. 3: An alternative to using → to retrieve data in nested structures, as Brian Carper described in his answer, is an input, along with its child and dependent members and an update: / p>

For example:

 (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
source share

You can force a struct to be the value of another structure if you give it a key that must be bound. You can do this as shown below.

(You can easily access the guts of arbitrarily nested hashes / structures through -> , like some syntactic sugar.)

 (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 
+6
source share

I am really new to clojure, so I could be wrong. But I think you cannot do something like

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

As far as I understand clojure structures, this would create a structure (mainly a map with known keys) that somehow structured the "rect" as one of its keys.

My assumption is supported by the observation that a simple estimate (struct rect) gives

 {:height nil, :width nil} 

While evaluation (struct color-rect) gives:

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

EDIT: What can help you is that structures are not limited to the keys with which they are defined. It seems that you can accomplish what you are trying to do something like this:

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

I understand this is an old question, but I came up with the following macro:

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

This will allow you to write the following:

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

Will this be what you wanted?

It can also be modified so that a collection of structures can be used. Or even allow other struct definitions to be used as key names that automatically expand into keys created by such a structure:

 (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} 
+1
source share

All Articles