Структура Clojure, вложенная в другую структуру - PullRequest
6 голосов
/ 17 февраля 2009

Возможно ли иметь структуру, вложенную в структуру в 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, я получаю «Слишком много аргументов для конструктора конструкций».

Если я подхожу к этой проблеме неправильно, пожалуйста, скажите мне, что я должен делать. Поиск в группе Google Clojure для меня ничего не дал.


Edit:

Полагаю, я не так ясно сформулировал свой вопрос, как мне показалось:

1.) Можно ли в Clojure вкладывать одну структуру в другую? (Судя по снизу это да.)

2.) Если так, каким будет правильный синтаксис? (Опять же, судя по снизу, похоже, есть несколько способов сделать это.)

3.) Как получить значение по указанному ключу, если у вас есть структура, вложенная в другую структуру?

Полагаю, мой пример кода не очень хорошо продемонстрировал то, что я пытался сделать. Я добавляю это сюда, чтобы другим, кто ищет это, было легче найти этот вопрос и ответы на него.

Ответы [ 5 ]

7 голосов
/ 17 февраля 2009

Я бы согласился с другими авторами в том, что структуры карт не поддерживают наследование. Однако, если вы хотите просто создать новую структуру, которая использует ключи другого, это будет работать:

; 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}
6 голосов
/ 18 февраля 2009

Вы можете сделать структуру значением другой структуры, если дадите ей ключ, с которым будете ассоциироваться. Вы можете сделать это, как показано ниже.

(Вы можете легко получить доступ к кишкам произвольно вложенных хэшей / структур через ->, как немного синтаксического сахара.)

(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 голосов
/ 17 февраля 2009

Вложенные конструкции возможны, а иногда и желательны. Однако, похоже, что вы пытаетесь сделать что-то другое: похоже, что вы пытаетесь использовать наследование структурных типов, а не композицию. То есть в форме 2 вы создаете цветовой прямоугольник, который содержит прямоугольник, но вы пытаетесь создать экземпляр, как если бы он был прямоугольником. Форма 1 работает, потому что вы создаете c-rect1 из ранее существовавшего прямоугольника, который является правильным способом использования композиции.

Быстрый поиск в группе Clojure или просто в Интернете в целом должен привести вас к хорошему описанию различия между составом и наследованием. В Clojure составление или набор текста утки (см. Google снова) почти всегда предпочтительнее наследования.


Edit:

В ответ на ваш вопрос № 3: альтернативой использованию -> для извлечения данных во вложенных структурах, как описал Брайан Карпер в своем ответе, является вход, наряду с его родственными связями и обновлением:

Например:

(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}
1 голос
/ 20 ноября 2011

Я понимаю, что это старый вопрос сейчас, но я придумал следующий макрос:

(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}
1 голос
/ 17 февраля 2009

Я действительно плохо знаком с 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
...