【问题标题】:Go - recursive struct with array and losing valuesGo - 具有数组和丢失值的递归结构
【发布时间】:2012-05-06 14:43:56
【问题描述】:

我创建了一个具有父级和子级的 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 &lt; len(el.children); i++,访问el.children[i]

不过,我想知道如何使用数组正确执行此操作。谢谢

【问题讨论】:

  • 你能把构建树的不同层次的代码贴出来吗?
  • 更新示例,包含完整的工作示例和输出的 cmets

标签: go


【解决方案1】:

解释为什么 []Element 版本不起作用的答案。

结构被复制为值。在SubElement 中,您创建一个Element 结构,然后在附加它时,实际上附加了该结构的全新副本。当您返回 el 并将其分配给 a 时,会生成另一个副本。 a 的地址不是附加的Element 的地址。

因此,您可能会变得棘手并获取实际在切片中的元素的地址,这甚至可能看起来像是在测试用例中工作,但是保留这些指针存在问题,例如当您执行将其存储回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
}

至少,这段代码有效;但是,如果您有其他代码使用指针,或者使用父指针,则必须重新考虑。

【讨论】:

  • 对我来说信息量很大(几乎没有 C 语言背景)。我正在处理基于缩进的模板,并且由于我当前的解析方式需要指向父级的东西。由于在可能包含变量缩进的两个单独嵌套下的同一缩进块中存在的两行无法提供足够的信息来确定父级,因此我在处理过程中为特定缩进点的最后一项保留了单独的参考树。我想整个混乱都会被垃圾收集,因为最终结果是字符串,但我现在当然有很多需要考虑的改进。再次感谢
【解决方案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>

【讨论】:

  • 确实,我尝试了类似的方法,使用children []*Element,然后使用parent.children = append(parent.children, &amp;el),但结果相同。使用el := &amp;Element{} 并在将SubElement(&amp;parent, "tag") 更改为SubElement(parent, "tag") 时返回*Element 似乎可行,但我不知道我的其他方法有什么问题。
  • 这也解决了一些其他问题,感谢代码示例
猜你喜欢
  • 2012-01-09
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 2014-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多