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 の Google グループを検索しても何も見つかりませんでした。


編集:

私の質問の文は、私が思っていたほど明確ではなかったと思います。

1.) Clojure である構造体を別の構造体内にネストすることは可能ですか? (下から判断すると、イエスです。)

2.) もしそうなら、正しい構文は何ですか? (繰り返しますが、下から判断すると、これを行う方法がいくつかあるようです。)

3.) 構造体が別の構造体内にネストされている場合、指定したキーで値を取得するにはどうすればよいですか?

私のサンプル コードは、私がしようとしていることをうまく示していなかったと思います。これをここに追加して、これを検索している他のユーザーがこの質問とその回答をより簡単に見つけられるようにします。

4

5 に答える 5

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 に答える
6

入れ子構造は可能であり、場合によっては望ましいものです。ただし、別のことをしようとしているようです。構成ではなく、構造型の継承を使用しようとしているようです。つまり、フォーム 2 では、rect を含むcolor-rect を作成しています、インスタンスを rect であるかのように構築しようとしています。フォーム 1 が機能するのは、既存の rect から c-rect1 を構築しているためです。これは、コンポジションを使用する正しい方法です。

Clojure グループまたは一般的な Web で簡単に検索すると、構成と継承の違いに関する適切な説明が得られるはずです。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 に答える
1

私はclojureに本当に慣れていないので、間違っているかもしれません。しかし、私は、あなたは次のようなことはできないと思います

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

私が clojure-structs を理解している限り、これは構造体 (基本的には既知のキーを持つマップ) を作成し、構造体の 'rect' をキーの 1 つとして持っています。

私の仮定は、 (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 に答える