嵌套在另一个结构中的Clojure结构

时间:2021-08-31 14:28:16

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

是否可以将结构嵌套在Clojure中的结构中?请考虑以下代码:

(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 where I want to break a large data structure into smaller substructures to make code easier to maintain. As the comments indicate if I do form 1 I get "249 nil nil" but if I do form 2 I get "Too many arguments to struct constructor".

当然这是一个人为的例子,但有些情况下我想将大型数据结构分解为更小的子结构,以使代码更易于维护。由于注释表明我是否形成1我得到“249 nil nil”但是如果我做表格2我得到“结构构造函数的参数太多”。

If I'm approaching this issue in the wrong way, please tell me what I should be doing. Searching the Clojure google group didn't turn up anything for me.

如果我以错误的方式处理这个问题,请告诉我我应该做什么。搜索Clojure google小组并没有为我提供任何帮助。


Edit:

I guess I wasn't as clear in the statement of my question as I thought I was:

我想我在问题陈述中并不像我想象的那样清楚:

1.) Is it possible to nest one struct within another in Clojure? (Judging from below that's a yes.)

1.)是否可以在Clojure中将一个结构嵌套在另一个结构中? (从下面判断,这是肯定的。)

2.) If so, what would be the correct syntax be? (Again, judging from below it looks like there are a few ways one could do this.)

2.)如果是这样,那么正确的语法是什么? (同样,从下面看,看起来有几种方法可以做到这一点。)

3.) How do you fetch a value by a specified key when you've got a struct nested within another struct?

3.)当你有一个嵌套在另一个结构中的结构时,如何通过指定的键获取值?

I guess my sample code didn't really demonstrate what I was trying to do very well. I'm adding this here so that others searching for this might find this question and its answers more easily.

我想我的示例代码并没有真正展示我想要做的很好。我在这里添加这个,以便其他人搜索这个可能更容易找到这个问题及其答案。

5 个解决方案

#1


I would agree with other posters in that struct maps don't really support inheritance. However, if you want to just make a new struct 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"
  ([c r] (apply struct (concat [color-rect c] (vals r))))
  ([c h w] (struct color-rect c h w)))

You don't need the echo-rect function, you can simply evaluate the struct map instance to see what's in it:

您不需要echo-r​​ect函数,您可以简单地评估struct map实例以查看其中的内容:

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}

#2


You can make a struct be a value of another struct if you give it a key to be associated with. You could do it as below.

如果给它一个与之关联的键,则可以使struct成为另一个struct的值。你可以这样做。

(You can easily access the guts of arbitrarily nested hashes/structs via ->, as a bit of syntax 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

#3


Nesting structures is possible and sometimes desirable. However, it looks like you're trying to do something different: It looks like you're trying to use inheritance of structure types rather than composition. That is, in form 2 you're creating a color-rect that contains a rect but you're trying to construct an instance as if it were a rect. Form 1 works because you're constructing c-rect1 from a pre-existing rect, which is the correct way to use composition.

嵌套结构是可能的并且有时是期望的。但是,看起来你正在尝试做一些不同的事情:看起来你正试图使用​​结构类型的继承而不是组合。也就是说,在表单2中,您正在创建一个包含rect的color-rect,但是您正在尝试构造一个实例,就好像它是一个rect。表单1的工作原理是因为您从预先存在的rect构造c-rect1,这是使用组合的正确方法。

A quick search on the Clojure group or just on the web in general should lead you to a good description of the distinction between composition and inheritance. In Clojure, composition or duck-typing (see Google again) is almost always preferred to inheritance.

快速搜索Clojure组或仅在Web上进行快速搜索可以使您对组合和继承之间的区别有一个很好的描述。在Clojure中,组合或鸭子打字(再次参见Google)几乎总是优先于继承。


Edit:

In answer to your Question #3: An alternative to using -> for extracting data in nested structures, as Brian Carper described in his answer, is get-in, along with its siblings assoc-in and update-in:

回答你的问题#3:使用 - >提取嵌套结构中的数据的替代方法,正如Brian Carper在他的回答中所描述的那样,与其兄弟姐妹关联和更新一起进入:

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}

#4


I am really new to clojure, so I might be wrong. But I think, you can't do something like

我对clojure很新,所以我可能错了。但我认为,你做不了类似的事情

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

As far as I understand clojure-structs, this would create a struct (basically a map with known keys), that has somehow the struct 'rect' as one of it's keys.

据我了解clojure-structs,这将创建一个struct(基本上是一个带有已知键的映射),它以某种方式将struct'rect'作为其中一个键。

My assumption is backed by the observation that a simple evaluation of (struct rect) yields

我的假设得到了(struct rect)简单评估的观察结果的支持

{:height nil, :width nil}

Whereas an evaluation of (struct color-rect) yields:

而(struct color-rect)的评估产生:

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

EDIT: What could help you is the fact, that structs are not limited to the keys, they are defined with. It appears as if you could accomplish, what you are trying by something like this:

编辑:有什么可以帮助你的事实是,结构不仅限于键,它们是定义的。看起来好像你可以完成,你正在通过这样的事情尝试:

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

#5


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

我现在意识到这是一个老问题,但我提出了以下宏:

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

Which would allow you to write this:

这将允许你写这个:

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

Would this be what you wanted?

这会是你想要的吗?

It could also be modified so that a collection of structures could be used. Or even allow you to use other struct definitions as names of keys, which are automatically expanded into the keys produced by such a 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}

#1


I would agree with other posters in that struct maps don't really support inheritance. However, if you want to just make a new struct 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"
  ([c r] (apply struct (concat [color-rect c] (vals r))))
  ([c h w] (struct color-rect c h w)))

You don't need the echo-rect function, you can simply evaluate the struct map instance to see what's in it:

您不需要echo-r​​ect函数,您可以简单地评估struct map实例以查看其中的内容:

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}

#2


You can make a struct be a value of another struct if you give it a key to be associated with. You could do it as below.

如果给它一个与之关联的键,则可以使struct成为另一个struct的值。你可以这样做。

(You can easily access the guts of arbitrarily nested hashes/structs via ->, as a bit of syntax 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

#3


Nesting structures is possible and sometimes desirable. However, it looks like you're trying to do something different: It looks like you're trying to use inheritance of structure types rather than composition. That is, in form 2 you're creating a color-rect that contains a rect but you're trying to construct an instance as if it were a rect. Form 1 works because you're constructing c-rect1 from a pre-existing rect, which is the correct way to use composition.

嵌套结构是可能的并且有时是期望的。但是,看起来你正在尝试做一些不同的事情:看起来你正试图使用​​结构类型的继承而不是组合。也就是说,在表单2中,您正在创建一个包含rect的color-rect,但是您正在尝试构造一个实例,就好像它是一个rect。表单1的工作原理是因为您从预先存在的rect构造c-rect1,这是使用组合的正确方法。

A quick search on the Clojure group or just on the web in general should lead you to a good description of the distinction between composition and inheritance. In Clojure, composition or duck-typing (see Google again) is almost always preferred to inheritance.

快速搜索Clojure组或仅在Web上进行快速搜索可以使您对组合和继承之间的区别有一个很好的描述。在Clojure中,组合或鸭子打字(再次参见Google)几乎总是优先于继承。


Edit:

In answer to your Question #3: An alternative to using -> for extracting data in nested structures, as Brian Carper described in his answer, is get-in, along with its siblings assoc-in and update-in:

回答你的问题#3:使用 - >提取嵌套结构中的数据的替代方法,正如Brian Carper在他的回答中所描述的那样,与其兄弟姐妹关联和更新一起进入:

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}

#4


I am really new to clojure, so I might be wrong. But I think, you can't do something like

我对clojure很新,所以我可能错了。但我认为,你做不了类似的事情

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

As far as I understand clojure-structs, this would create a struct (basically a map with known keys), that has somehow the struct 'rect' as one of it's keys.

据我了解clojure-structs,这将创建一个struct(基本上是一个带有已知键的映射),它以某种方式将struct'rect'作为其中一个键。

My assumption is backed by the observation that a simple evaluation of (struct rect) yields

我的假设得到了(struct rect)简单评估的观察结果的支持

{:height nil, :width nil}

Whereas an evaluation of (struct color-rect) yields:

而(struct color-rect)的评估产生:

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

EDIT: What could help you is the fact, that structs are not limited to the keys, they are defined with. It appears as if you could accomplish, what you are trying by something like this:

编辑:有什么可以帮助你的事实是,结构不仅限于键,它们是定义的。看起来好像你可以完成,你正在通过这样的事情尝试:

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

#5


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

我现在意识到这是一个老问题,但我提出了以下宏:

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

Which would allow you to write this:

这将允许你写这个:

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

Would this be what you wanted?

这会是你想要的吗?

It could also be modified so that a collection of structures could be used. Or even allow you to use other struct definitions as names of keys, which are automatically expanded into the keys produced by such a 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}