我正在阅读使用ruby构建域爬虫方法的人的代码。我是递归概念的新手,无法理解如何阅读他们的代码。
代码:
def crawl_domain(url, page_limit = 100)
return if @already_visited.size == page_limit # [1]
url_object = open_url(url)
return if url_object == nil # [2]
parsed_url = parse_url(url_object)
return if parsed_url == nil # [3]
@already_visited[url]=true if @already_visited[url] == nil
page_urls = find_urls_on_page(parsed_url, url)
page_urls.each do |page_url|
if urls_on_same_domain?(url, page_url) and @already_visited[page_url] == nil
crawl_domain(page_url)
end
end
end
问题:
return
语句的组合是什么意思? url_object = open_url(url)
提前感谢您的帮助!
来源: http://www.skorks.com/2009/07/how-to-write-a-web-crawler-in-ruby/
答案 0 :(得分:3)
忘记网络抓取工具。考虑如何使用递归来添加构成数组的数字:
arr = [1, 2, 3, 4]
def sum(array)
# ??
end
puts sum(arr) #=> 10, please
假装问题已经解决。 (所有递归都取决于假装。)然后sum([1, 2, 3, 4])
为1 + sum([2, 3, 4])
,对于任何数组,通常sum
是第一个元素加上sum
的数组的其余部分。在ruby中,将数组拆分为第一个及其余数的方法是shift
;调用shift
返回数组的第一个元素,将其从数组中删除。所以我们可以写:
arr = [1, 2, 3, 4]
def sum(array)
return array.shift + sum(array)
end
puts sum(arr)
看,一个递归的解决方案!然而,有一个问题:我们永远递归(或者,至少,直到遇到某种错误)。在所有递归中,在递归之前在退化情况中放入某种“停止”是至关重要的。在我们的情况下,这种堕落的情况是什么?这是阵列空的时候! (最终将是这样,因为每个sum
调用都会从数组中删除一个元素。)对于空数组,显然sum
为零。所以:
arr = [1, 2, 3, 4]
def sum(array)
return 0 unless array.length > 0
return array.shift + sum(array)
end
puts sum(arr)
结束。
您的网络抓取工具类似。我们将递归以抓取当前页面的每个子级别,但首先我们有一些“停止”(return
语句)以防止在各种退化情况下敲入错误情况。 (对于网络爬虫,没有必要检查当前页面是否实际上有子级别,因为如果没有,each
将无所作为,我们赢了“ t recurse。)
答案 1 :(得分:2)
答案 2 :(得分:1)
递归只是意味着该方法至少调用一次以完成它的任务。有时,算法可以比迭代地更加干净地表达。对于域抓取工具,您可以想象一旦您知道如何抓取顶级页面,就可以对每个内部链接使用相同的技术:
def crawl_domain(url)
page_urls = find_urls_on_page(url)
page_urls.each do |page_url|
if urls_on_same_domain?(url, page_url)
# We can assume the method that works for the top level will work
# for all the urls on the page too.
crawl_domain(page_url)
end
end
end
这是该方法的核心,但它有一个主要问题:它永远不会结束。 (好吧,如果它所登陆的页面没有任何内部链接,它就会结束。)这就是为什么(回答问题#1)该方法有一些早期的返回调用。如果调用任何返回,则该方法不会调用自身。
要回答问题#2,return
只会突破方法if @already_visited.size == page_limit
。请注意,@already_visited
会跟踪方法访问过的网址,以避免再次访问它们。哈希的大小还会告诉您访问过的网址数量。因此,如果网址数达到page_limit
,则第一次返回会将您踢出该方法。 (page_limit
默认为100,但奇怪的是递归调用不提供它提供的限制。我认为这可能是一个错误。)
对于你的第三个问题,你似乎误解了conditional modifiers。如果条件为真,则该语句仅执行 。因此,只有在尚未达到visited-url限制时,该方法才会继续。
我认为一个主要的混淆点是,在函数的 end 处看到一个返回调用更为常见。有些人甚至advocate a single exit point。但是最好使用自调用来构造递归方法:tail recursion。因此,使用递归方法,您会经常看到早期的回复呼叫提前纾困。由于在深度嵌套的条件中间很难发现那些return
,因此它们倾向于使用这种基本格式:
return if some_reason_to_bail == true