Recursion 对于Go中的递归函数,如果内部函数返回,外部函数是否正常继续执行?
好的,我有这段代码Recursion 对于Go中的递归函数,如果内部函数返回,外部函数是否正常继续执行?,recursion,go,dereference,panic,Recursion,Go,Dereference,Panic,好的,我有这段代码 func registerDomain(domainName string, n int) bool { //building the request here resp, errr := client.Do(r) if errr != nil { if n == 1 { return false } registerDomain(domainName, n-1) }
func registerDomain(domainName string, n int) bool {
//building the request here
resp, errr := client.Do(r)
if errr != nil {
if n == 1 {
return false
}
registerDomain(domainName, n-1)
}
bodyBytes, err2 := ioutil.ReadAll(resp.Body)
if err2 == nil {
resp.Body.Close()
//handle bodyBytes
//if the response is how it should be return true, if it's not call the function again with n-1
} else { //if there is an error reading the response
resp.Body.Close()
if n == 1 {
return false
}
registerDomain(domainName, n-1)
}
return false //it should never reach this line
}
说明:
我用一个参数n(比如5)调用函数,该参数表示函数在出现错误时重试的次数。每次出现问题时,我都使用n-1进行递归调用,因此当它达到n=1时,它就会放弃并返回false。这段代码在实践中工作得很好,它完成了它应该做的事情,有时当响应不正确时,它会递归地调用自己,并在第二次(或第三次、第四次…)工作。当问题在n=1之前未修复时,返回false
问题是:
这是一个大代码的一部分,该代码应该运行17个小时左右,当它试图从身体中读取时,它会感到恐慌,有时在这行:
bodyBytes, err2 := ioutil.ReadAll(resp.Body)
它表示panic:runtime错误:无效内存地址或零指针解引用。现在我知道这可能意味着它试图从一个不存在的resp.Body读取,但它清楚地指出,当err为nil时,resp总是包含一个非nil resp.Body
所以,在我看来,这可能是一些递归调用。对我来说唯一有意义的是这个场景:假设errr不是nil(这意味着resp.Body不存在),所以它进入if errr!=零,因为n=1,它将再次调用自身,n=4。让我们假设这一次一切正常,第二个函数返回第一个函数的true,但第一个函数继续执行,并尝试从不存在的resp.Body读取。这引起了恐慌,我们现在
所以,我需要的是有人确切地知道递归函数是如何工作的,如果不是这样,我可以在阅读表单之前检查resp.Body的存在,或者做一些有帮助的事情
无论如何,谢谢!:)
更新:你很好,我的代码不再恐慌,我也不会。非常感谢!(我不确定这是否是更新的地方)您需要在第一次registerDomain调用后返回,否则当您对registerDomain的调用完成时,您将在ioutil.ReadAll上死机
//building the request here
resp, errr := client.Do(r)
if errr != nil {
if n == 1 {
return false
}
return registerDomain(domainName, n-1)
}
改变
registerDomain(domainName, n-1)
到
这样,外部函数将不会继续执行,也不会从nil body读取。在
registerDomain()函数中,有两个地方可以调用自身
如果client.Do()
失败(errr!=nil
),您将再次调用registerDomain()
。有时它会返回,当它返回时,您的代码将继续执行,尝试从resp.Body
读取,但这很可能是nil
,因为errr!=无
以下是您应该如何处理它:
无论何时进行递归调用,当调用返回时,不应让代码继续,而应返回它返回的值,如下所示:
return registerDomain(domainName, n-1)
可供替代的
您试图解决的问题可以使用for
语句在不递归的情况下解决。
的变体将更加清晰和简单
修改registerDomain()
函数,使其在每次调用自身时返回false,并使用此循环重试5次:
var result bool
for i := 0; i < 5; i++ {
result = registerDomain(domainName) // You don't need to pass n anymore
if result {
break
}
}
if result {
fmt.Println("Domain registered successfully!")
} else {
fmt.Println("Failed to register domain!")
}
var结果bool
对于i:=0;i<5;i++{
result=registerDomain(域名)//不再需要传递n
如果结果{
打破
}
}
如果结果{
fmt.Println(“域注册成功!”)
}否则{
fmt.Println(“注册域失败!”)
}
它是立即死机,还是在一段时间后死机?在一段时间后,大约执行4小时。所以这一定是一个非常具体的场景。是的,这非常适合,我会尝试它,如果这是解决方案,我会在执行后更新。谢谢非常感谢。我会尝试在registerDomain递归调用之前添加return,如果这是个问题,我会更新这篇文章,但我相信这是真的,因为它非常有意义。我已经放心了,因为现在所有的答案(3)都在暗示同样的事情。谢谢-我将尝试并更新。
var result bool
for i := 0; i < 5; i++ {
result = registerDomain(domainName) // You don't need to pass n anymore
if result {
break
}
}
if result {
fmt.Println("Domain registered successfully!")
} else {
fmt.Println("Failed to register domain!")
}