代码之家  ›  专栏  ›  技术社区  ›  Onorio Catenacci

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

  •  6
  • Onorio Catenacci  · 技术社区  · 16 年前

    在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 Google组并没有找到任何适合我的东西。


    编辑:

    我想我的问题陈述不像我想象的那么清楚:

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

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

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

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

    5 回复  |  直到 13 年前
        1
  •  7
  •   pjb3    16 年前

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

    ; 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 函数,您可以简单地计算结构映射实例以查看其中的内容:

    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
  •  6
  •   Brian Carper    16 年前

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

    (您可以通过 -> 作为一点语法糖。)

    (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
  •  6
  •   Nathan Kitchen    16 年前

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

    快速搜索Clojure组或者仅仅是在Web上,一般来说应该能够很好地描述组合和继承之间的区别。在clojure中,构图或duck类型(再次参见google)几乎总是优先于继承。


    编辑:

    回答您的问题3:如Brian Carper在回答中所述,使用->提取嵌套结构中的数据的另一种选择是:与其兄弟体关联并更新:

    例如:

    (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
  •  1
  •   Mo.    16 年前

    我对Clojure真的很陌生,所以我可能错了。但我想你不能做这样的事

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

    就我所了解的clojure结构而言,这将创建一个结构(基本上是一个具有已知键的映射),它以某种方式将结构“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
    
        5
  •  1
  •   LiamGoodacre    13 年前

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

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