我很惊讶地发现Go语言有一个'goto'语句。我一直被教导说,'goto'语句是过时的,会使程序的实际流程变得模糊,而函数或方法始终是控制流程的更好方式。
我肯定是没get到什么。为什么Google要把它包含在里面呢?
当我们实际检查Go标准库的源代码时,可以看到goto
语句实际上被很好地应用。
例如,在math/gamma.go
文件中,使用了goto
语句:
for x < 0 {
if x > -1e-09 {
goto small
}
z = z / x
x = x + 1
}
for x < 2 {
if x < 1e-09 {
goto small
}
z = z / x
x = x + 1
}
if x == 2 {
return z
}
x = x - 2
p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6]
q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7]
return z * p / q
small:
if x == 0 {
return Inf(1)
}
return z / ((1 + Euler*x) * x)
}
在这种情况下,goto
语句可以避免我们引入另一个(布尔)变量用于控制流,并在结尾处进行检查。在这种情况下,goto
语句使得代码实际上更易阅读和理解(与您提到的反对goto
的观点相反)。goto
语句具有非常特定的用例。关于goto
的语言规范指出,它不能跳过正在进入作用域(被声明)的变量,并且不能跳转到其他(代码)块中。small(x,z)
来调用呢?这样我们就不必考虑small:
标签中哪些变量是可访问的了。我猜测原因是Go语言编译器仍缺乏某些类型的内联支持。 - Thomas Ahlegoto
指向标签。在执行"goto"语句时,不能使任何变量进入作用域,这些变量在goto语句的位置之前不在作用域内。参考文献:https://golang.org/ref/spec#Goto_statements - km6zla当内置的控制功能无法满足您的需求,或者您可以使用Goto表达您想要的内容时,使用Goto是一个不错的选择。(在某些语言中,如果没有Goto,您最终会滥用某些控制功能、使用布尔标志或使用比Goto更糟糕的其他解决方案,这是一件可惜的事情。)
如果其他控制功能(以合理明显的方式使用)可以实现您想要的内容,则应优先使用它,而不是使用Goto。如果没有,那就勇敢地使用Goto吧!
最后值得注意的是,Go语言的Goto有一些限制,旨在避免一些晦涩的错误。请参阅规范中的这些限制。
goto
是与continue
、break
和return
相同技术方式的跳转语句。有人可能会认为这些语句也是邪恶的,但它们并不是。continue
、break
和return
在一个关键点上非常不同:它们仅指定“离开封闭范围”。它们不仅鼓励而且明确要求开发人员考虑其代码的结构并依赖于结构化编程原语(例如循环,函数和switch语句)。goto
语句的唯一救赎是当编译器的优化器无法胜任时,它允许您在HLL中编写汇编代码,但这是以可读性和可维护性为代价的。 - Parthian Shotsetjmp
、longjmp
、goto
和try/except/finally
)之间有选择时,他们选择了谨慎。据我所知,goto
是唯一对“结构化编程”控制流的妥协。 - Parthian Shot
goto
很有用。请在学习Kissaki的答案后阅读。 - kostixGo
没有像异常流那样的高级控制流结构,因此自然而然地需要通过使用goto
来进行补偿。 - Ben