2012-06-16 106 views
62

Google的Go语言是一种新语言。因此,我惊讶地发现它有一个'goto'声明。我总是被教导说'goto'陈述是过去的事情,它的邪恶阻碍了程序的实际流程。功能(或方法,如果你愿意的话)总是控制流量的更好方法。为什么Go有一个“goto”语句

我必须缺少一些东西。为什么和何时使用'goto'是一个好主意?或者为什么Google包含它?

+2

有些时候你确实需要一个goto语句。只有在不加选择地使用后藤才是邪恶的。例如,如果非常困难,即使不是不可能的话,编写一个有限状态机解析器并带有语句。 – xbonez

+3

这不是Go所特有的,但是为了讨论为什么语言保留语句以及查看反对它的使用的论点,请查看[this post](http://stackoverflow.com/questions/46586/goto-still-认为有害)。这个问题中有一些很好的参考资料。编辑:[这是另一个](http://stackoverflow.com/questions/24451/goto-usage)。 –

+0

为了通过提供的SO讨论保存操作系统,请参阅[关于LKML的讨论](http://kerneltrap.org/node/553/2131),它几乎总结了为什么'goto'在某些情况下很有用。学习@ Kissaki的回答后阅读。 – kostix

回答

48

这些限制,当我们实际检查GOS源代码(标准库),我们可以看到goto s的实际很好的应用。

例如,在math/gamma.go文件,the goto statement is used

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声明具有非常具体的用例。 language specification on goto指出它不能跳过进入作用域的变量(被声明),并且它可能不会跳转到其他(代码)块中。

+18

如果没有'goto',那么代码的可读性会降低,至少会更长。这会让被洗脑的Java程序员感到惊讶。 –

+34

在你的例子中,为什么不引入一个函数'small(x,z)'来代替?这样我们就不必考虑在'small:'标签中可以访问哪些变量。我怀疑原因是在编译器中仍然缺乏某些类型的内联支持。 –

+0

@Thomas,我很确定这是因为在很多情况下缺乏内联。函数调用很重要的计算密集型应用程序往往包含大量的数学运算,所以在数学包中使用goto是相当合理的。 –

15

Goto是一个好主意,当内置的控制功能没有任何内容完成您想要的功能时,以及何时可以用goto表达您想要的内容。 (在这些情况下,在某些语言中,当你没有转到时,这是一种耻辱,最终会滥用某些控制功能,使用布尔标志或者使用比goto更差的其他解决方案。)

如果某些其他控制功能以相当明显的方式使用)可以做你想做的,你应该优先使用它。如果没有,请大胆使用goto!

最后值得注意的是Go的goto有一些限制,旨在避免一些晦涩的bug。见the spec.

1

自上世纪60年代到70年代Spaghetti code时代以来,Goto的声誉受到了很多怀疑。当时那里的软件开发方法非常差。然而,Goto并不是天生的邪恶,但当然可以被懒惰或不熟练的程序员滥用和滥用。滥用Gotos的许多问题可以通过团队代码审查等开发流程解决。

goto以与continue,breakreturn相同的技术方式跳转。人们可以争辩说,这些陈述是相同的方式,但他们不是。

Go团队为什么包含Gotos可能是因为它是一个常见的流控制原语。此外,他们希望得出结论:围棋的范围不包括制造一种不适合滥用白痴的语言。

-2

据我所知,goto主要用于在单个函数内部实现巨型状态机。现代软件开发倾向于通过将状态机分解为类和多种方法来实现状态机,但是有些人不相信编译器优化代码,因此他们希望手动将所有内容嵌入到一个巨大的函数中。

+0

有时候,在游戏开发中,即使是函数调用也是巨大的开销。当你关心RAW性能时,单个堆栈框架会导致巨大的性能开销,而且你确实在实施FSM,或者你有XYZ循环,并且你想从内部分解它,goto是你唯一的朋友。 –

+0

是的,但一个好的编译器应该为你做优化。不幸的是,Go的编译器支持优化编译速度。 – Antimony

+0

不幸的是,编译器通常不喜欢我们,特别是在内联级别。宏仍然是最好的解决方案......“goto”仍然是重要的主要特征之一。如果你尝试移植一个C库,你将会经常决定保持现代语言的格式。如果你编写语言翻译器,比如IL2CPP,goto是剩余的唯一工具,可以轻松实现分支。 [好吧,这样生成的代码仍然很丑陋,它不适合人类阅读] –