1

親と子を持つElement構造体を作成し、SubElementと呼ばれるヘルパー関数を作成し、印刷のためにすべての子を反復処理するStringメソッドを作成しました。

package main

import "fmt"

type Element struct {
  parent *Element
  children []Element
  tag string
}

func SubElement(parent *Element, tag string) Element {
  el := Element{}
  el.parent = parent
  el.tag = tag
  parent.children = append(parent.children, el)
  return el
}

func (el Element) String() string {
  s := "<" + el.tag + ">"
  for _, child := range el.children {
    s += child.String()
  }
  s += "</" + el.tag + ">"
  return s
}

func main() {
  root := Element{}
  root.tag = "root"

  a := SubElement(&root, "a")
  b := SubElement(&a, "b")
  SubElement(&b, "c")

  fmt.Println(root) // prints: <root><a></a></root>
  fmt.Println(a) // prints: <a><b></b></a>
  // and so on
}

私が経験している問題は、印刷することを選択したルートノードから子の最初の層しか利用できないことです。これは、parent.childrenでのappendの使用に関連していると確信していますが、これを正しく解決する方法についての理解が不足しています。

この問題を回避するために、に変更childrenしましたmap[int]Element。次に、SubElement関数で、を「追加」しparent.children[len(parent.children)] = elます。次に、正しい順序で反復するために、Stringメソッドのfor-loopはfor i:= 0; i < len(el.children); i++、にアクセスしel.children[i]ます。

それでも、配列を使用してこれを正しく行う方法を知りたいです。ありがとう

4

2 に答える 2

5

[]Elementバージョンが機能しなかった理由を説明するための回答。

構造体は値としてコピーされます。ではSubElement、1つの構造体を作成しElement、それを追加すると、実際には構造体のまったく新しいコピーが追加されます。elを返し、に割り当てるとa、さらに別のコピーが作成されます。のアドレスは、追加されたのアドレスでaはありません。Element

したがって、トリッキーになって、実際にスライスにある要素のアドレスを取得する可能性があり、テストケースで機能しているように見える場合もありますが、保存するときのように、これらのポインタを1つ保持することには問題があります。でElement.parent。問題は、後続の追加でスライスが再割り当てされる可能性があり、保持されているポインタが現在有効なスライスではなく、孤立したメモリを指していることです。

[] * Elementバージョンが他のいくつかの問題を解決した場合、それらは、その後に孤立したポインターの保管の問題であった可能性があります。

構造体のスライスを使用してツリーを実装することは可能ですが、ポインターをスライスに保持することは通常間違いであることを明確に理解する必要があります。親ポインタの保存は安全ではないため、構造体から削除することをお勧めします。

package main

import "fmt"

func main() {
    tree := Element{tag: "head"}
    tree.SubElement("tier-1")
    tree.children[0].SubElement("tier-2")
    tree.children[0].SubElement("tier-2")
    tree.SubElement("tier-1")
    tree.children[1].SubElement("tier-2")
    fmt.Println(tree)
}

type Element struct {
    children []Element
    tag      string
}

func (parent *Element) SubElement(tag string) {
    parent.children = append(parent.children, Element{tag: tag})
}

func (el Element) String() string {
    s := "<" + el.tag + ">"
    for _, child := range el.children {
        s += child.String()
    }
    s += "</" + el.tag + ">"
    return s
}

このコードは、少なくとも機能します。ただし、ポインターを処理している、または親ポインターを使用している他のコードがある場合は、再考する必要があります。

于 2012-04-25T23:48:48.973 に答える
2

最初の手がかりは、SubElementが(元々)そこにあるようにコンパイルされないことです(編集:持っていました)。それを機能させることを試すことができますが、Element.childrenをで[]*Elementはなくに変更することをお勧めします[]Element。実例は次のとおりです。

package main

import "fmt"

func main() {
    tree := &Element{tag: "head"}
    t1 := SubElement(tree, "tier-1")
    SubElement(t1, "tier-2")
    SubElement(t1, "tier-2")
    t1 = SubElement(tree, "tier-1")
    SubElement(t1, "tier-2")
    fmt.Println(tree)
}

type Element struct {
    parent   *Element
    children []*Element
    tag      string
}

func SubElement(parent *Element, tag string) *Element {
    el := &Element{parent: parent, tag: tag}
    parent.children = append(parent.children, el)
    return el
}

func (el *Element) String() string {
    s := "<" + el.tag + ">"
    for _, child := range el.children {
        s += child.String()
    }
    s += "</" + el.tag + ">"
    return s
}

出力:

<head><tier-1><tier-2></tier-2><tier-2></tier-2></tier-1><tier-1><tier-2></tier-2></tier-1></head>
于 2012-04-25T22:43:22.920 に答える