当找到元素时停止递归函数。

huangapple go评论93阅读模式
英文:

Stop recursive function when element is found

问题

我正在尝试解决《The Go Programming Language》一书中的一个练习题:
起始代码可以在这里找到: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函数是相同的,只是pre和post的签名被更改为返回一个布尔值。

英文:

I am trying to solve an exercise from The go programming language book:
The starting code can be found here: exercise.

What I need to do:

Modify forEachNode so that the pre and post functions return a boolean result indicating whether to continue the traversal. Use it to write a function ElementByID with the following signature that finds the first HTML element with the specified id attribute. The function should stop the traversal as soon as a match is found.

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

What I did:

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

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

Is the above right?, or I've missed something? How can I stop the traversal where element is found?

The forEachNode is the same, only the pre and post signature was changed to return a bool.

答案1

得分: 1

你可以创建一个闭包并“关闭”found节点。下面是一个示例。

修改forEachNode函数,使得prepost函数返回一个布尔值,指示是否继续遍历:

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 // 记录匹配的节点
                return false // 停止遍历
            }
        }
        return true
    }

    forEachNode(doc, pre, nil)
    return found
}
英文:

You can create a closure and "close" found node. Example below.

> Modify forEachNode so that the pre and post functions return a boolean result indicating whether to continue the traversal.:

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

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

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

> Use it to write a function ElementByID with the following signature that finds the first HTML element with the specified id attribute. The function should stop the traversal as soon as a match is found.:

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 == &quot;id&quot; &amp;&amp; a.Val == id {
				found = n // memorize matching node
				return false // stop traversing
			}
		}
		return true
	}

	forEachNode(doc, pre, nil)
	return found
}

huangapple
  • 本文由 发表于 2016年1月9日 21:53:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/34694295.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定