6

是否可以在 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)

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

如果我以错误的方式处理这个问题,请告诉我我应该做什么。搜索 Clojure 谷歌组并没有为我找到任何东西。


编辑:

我想我在问题的陈述中并没有我想的那么清楚:

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

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

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

我想我的示例代码并没有真正证明我想要做得很好。我在此处添加此内容,以便其他搜索此内容的人可以更轻松地找到此问题及其答案。

4

5 回答 5

7

我同意其他海报中的 struct maps 并不真正支持继承。但是,如果您只想创建一个使用另一个键的新结构,这将起作用:

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

您不需要该echo-rect函数,您可以简单地评估 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}
于 2009-02-17T14:03:01.067 回答
6

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

在 Clojure 组或一般的网络上进行快速搜索应该可以很好地描述组合和继承之间的区别。在 Clojure 中,组合或鸭子类型(再次参见 Google)几乎总是比继承更受欢迎。


编辑:

回答您的问题 #3:正如 Brian Carper 在他的回答中所描述的,使用 -> 提取嵌套结构中的数据的替代方法是 get-in,以及它的兄弟姐妹 assoc-in 和 update-in:

例如:

(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}
于 2009-02-17T00:04:23.067 回答
6

如果你给它一个要关联的键,你可以使一个结构成为另一个结构的值。您可以按如下方式进行。

(您可以通过 轻松访问任意嵌套的哈希/结构->的内容,作为一些语法糖。)

(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
于 2009-02-18T19:59:20.780 回答
1

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

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

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

我的假设得到了观察结果的支持,即对 (struct rect) 的简单评估会产生

{:height nil, :width nil}

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

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

编辑:可以帮助你的是,结构不限于键,它们是用定义的。似乎您可以通过以下方式完成您正在尝试的事情:

(def c-rect1 (struct-map color-rect :color 249 :height 1 :width 1 )) ;form 3
于 2009-02-17T00:01:59.840 回答
1

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

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

这将允许你这样写:

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

测试:

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

这会是你想要的吗?

它也可以被修改,以便可以使用结构的集合。甚至允许您使用其他结构定义作为键的名称,这些名称会自动扩展为此类结构生成的键:

(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}
于 2011-11-20T00:57:45.207 回答