Go中的递归函数,如果内层函数returns,外层函数是否继续正常执行?
With recursive functions in Go, if the inner function returns, does the outer function continue execution normally?
好的,所以我有这段代码
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 时,它会放弃并且 returns false。这段代码在实践中运行良好,它做了它应该做的事情,有时当响应不正确时它递归地调用自己并且它在第二次(或第三次,第四次......)时工作。如果问题在 n=1 之前未解决,则 returns 错误。
问题:
这是一个大代码的一部分,它应该 运行 大约 17 个小时,当它试图从正文中读取时,它有时会在这一行出现恐慌:
bodyBytes, err2 := ioutil.ReadAll(resp.Body)
它说恐慌:运行时间错误:无效的内存地址或零指针取消引用。现在我知道这可能意味着它试图从一个不存在的 resp.Body 中读取,但是 Go documentation 明确指出 当 err 为 nil 时,resp 总是包含一个非 nil resp.Body。
所以,在我看来,这可能与递归调用有关。 only 对我来说有意义的是这种情况:可以说 errr 不是零(这意味着 resp.Body 不存在),所以它进入了内部的 if errr != nil 并且因为 n!=1,它会再次用 n=4 调用自己。假设这次一切都应该是这样,第二个函数 returns 对第一个函数 但是 第一个函数继续执行并尝试从 resp.Body 不存在。这会引起恐慌,我们在这里...
所以,我需要的是一个确切知道递归函数如何工作的人,如果不是,我可以在阅读之前以某种方式检查 resp.Body 是否存在,或者有帮助的东西。
总之谢谢! :)
更新: 你没事,我的代码不再恐慌了,我也没有。非常感谢! (我不确定这是不是更新的地方)
您需要在第一次调用 registerDomain 后 return ,否则当您对 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)
到
return registerDomain(domainName, n-1)
这样外部函数将不会继续执行,也不会从 nil 主体中读取。
在 registerDomain()
函数中有 2 个地方可以调用它自己。
如果client.Do()
失败(errr != nil
),您将再次调用registerDomain()
。有时它会 return,当它发生时,您的代码执行将继续,尝试从 resp.Body
读取,但最有可能是 nil
因为 errr != nil
.
以下是您应该如何处理它:
每当你进行递归调用时,你不应该让代码在调用 returns 时继续,而是 return 它 returned 的值,像这样:
return registerDomain(domainName, n-1)
备选
您尝试解决的问题可以使用 for
语句在不递归的情况下解决。 for
变体甚至会更清晰、更简单。
将你的 registerDomain()
函数修改为 return 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!")
}
好的,所以我有这段代码
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 时,它会放弃并且 returns false。这段代码在实践中运行良好,它做了它应该做的事情,有时当响应不正确时它递归地调用自己并且它在第二次(或第三次,第四次......)时工作。如果问题在 n=1 之前未解决,则 returns 错误。
问题:
这是一个大代码的一部分,它应该 运行 大约 17 个小时,当它试图从正文中读取时,它有时会在这一行出现恐慌:
bodyBytes, err2 := ioutil.ReadAll(resp.Body)
它说恐慌:运行时间错误:无效的内存地址或零指针取消引用。现在我知道这可能意味着它试图从一个不存在的 resp.Body 中读取,但是 Go documentation 明确指出 当 err 为 nil 时,resp 总是包含一个非 nil resp.Body。
所以,在我看来,这可能与递归调用有关。 only 对我来说有意义的是这种情况:可以说 errr 不是零(这意味着 resp.Body 不存在),所以它进入了内部的 if errr != nil 并且因为 n!=1,它会再次用 n=4 调用自己。假设这次一切都应该是这样,第二个函数 returns 对第一个函数 但是 第一个函数继续执行并尝试从 resp.Body 不存在。这会引起恐慌,我们在这里...
所以,我需要的是一个确切知道递归函数如何工作的人,如果不是,我可以在阅读之前以某种方式检查 resp.Body 是否存在,或者有帮助的东西。
总之谢谢! :)
更新: 你没事,我的代码不再恐慌了,我也没有。非常感谢! (我不确定这是不是更新的地方)
您需要在第一次调用 registerDomain 后 return ,否则当您对 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)
到
return registerDomain(domainName, n-1)
这样外部函数将不会继续执行,也不会从 nil 主体中读取。
在 registerDomain()
函数中有 2 个地方可以调用它自己。
如果client.Do()
失败(errr != nil
),您将再次调用registerDomain()
。有时它会 return,当它发生时,您的代码执行将继续,尝试从 resp.Body
读取,但最有可能是 nil
因为 errr != nil
.
以下是您应该如何处理它:
每当你进行递归调用时,你不应该让代码在调用 returns 时继续,而是 return 它 returned 的值,像这样:
return registerDomain(domainName, n-1)
备选
您尝试解决的问题可以使用 for
语句在不递归的情况下解决。 for
变体甚至会更清晰、更简单。
将你的 registerDomain()
函数修改为 return 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!")
}