找到元素时停止递归函数

时间:2016-01-09 13:53:12

标签: go

我正在尝试使用The go编程语言书来解决这个问题: 可以在此处找到起始代码:exercise

我需要做什么:

修改forEachNode,以便pre和post函数返回一个布尔结果,指示是否继续遍历。使用它来编写具有以下签名的函数ElementByID,该签名查找具有指定id属性的第一个HTML元素。一旦找到匹配项,该函数应立即停止遍历。

签名:func ElementByID(doc *html.Node, id string) *html.Node

我做了什么:

func ElementByID(doc *html.Node, id string) *html.Node {                                                         
  if doc.Data == id {                                                                                            
    fmt.Printf(" %s: %s\n", "found", doc.Data)                                                                   
    return doc                                                                                                   
  }                                                                                                              
  return nil                                                                                                     
}

func startElement(n *html.Node) bool {
  if n.Type == html.ElementNode {
    if ElementById(n, "a") != nil {
      return true
    }
    fmt.Printf("%*s<%s>\n", depth*2, "", n.Data)
    depth++
  }
  return false
}
func endElement(n *html.Node) bool {
  if n.Type == html.ElementNode {
    if ElementById(n, "a") != nil {
      return true
    }
    depth--
    fmt.Printf("%*s</%s>\n", depth*2, "", n.Data)
  }
  return false
}

上面是对吗?还是我错过了什么?如何停止遍历元素的遍历?

forEachNode是相同的,只有前后签名被更改为返回一个bool。

1 个答案:

答案 0 :(得分:1)

您可以创建closure和“关闭”found节点。示例如下。

  

修改forEachNode,使pre和post函数返回一个布尔结果,指示是否继续遍历。:

func forEachNode(n *html.Node, pre, post func(n *html.Node) bool) {
    if pre != nil && !pre(n) {
        return
    }

    for c := n.FirstChild; c != nil; c = c.NextSibling {
        forEachNode(c, pre, post)
    }

    if post != nil && !post(n) {
        return
    }
}
  

使用它来编写具有以下签名的函数ElementByID,该签名查找具有指定id属性的第一个HTML元素。一旦找到匹配项,该函数应立即停止遍历。

func ElementByID(doc *html.Node, id string) *html.Node {

    var found *html.Node

    pre := func(n *html.Node) bool {
        for _, a := range n.Attr {
            if a.Key == "id" && a.Val == id {
                found = n // memorize matching node
                return false // stop traversing
            }
        }
        return true
    }

    forEachNode(doc, pre, nil)
    return found
}