Go为什么有“ goto”语句


110

我惊讶地发现Go带有“ goto”语句。我一直被教导说,“ goto”语句已经成为过去,并且因为它阻塞了程序的实际流程,所以它变得邪恶,并且函数或方法始终是控制流程的更好方法。

我肯定错过了什么。Google为什么要包含它?


5
有时您确实需要goto语句。仅当不加选择地使用后藤时,它才是邪恶的。例如,如果很难(如果不是不可能的话)编写带有goto语句的有限状态机解析器。
xbonez 2012年

5
它不是特定于Go的,但是要很好地讨论为什么语言会保留该语句,并查看反对使用该语句的论点,请查看这篇文章。问题中有一些很好的参考链接。编辑:这是另一个
2012年

3
为了避免OP通过提供的SO讨论而陷入困境,这里是关于LKML的讨论,该讨论几乎总结了为什么goto在某些情况下有用。在学习@Kissaki的答案后阅读。
kostix 2012年


这对实现延续模式很有用,您可以在其中保存堆栈,然后返回到要恢复的位置。
贾斯汀·丹纳豪威尔

Answers:


78

当我们实际检查Go标准库的源代码时,我们可以看到gotos在哪些地方得到了很好的应用。

例如,在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语言规范指出,它可能不会跳过进入范围(被声明)的变量,并且可能不会跳过其他(代码)块。


69
在您的示例中,为什么不引入一个函数small(x,z)来代替呢?这样,我们不必考虑small:标签中可以访问哪些变量。我怀疑原因是go仍然缺少编译器中某些类型的内联支持。
Thomas Ahle 2013年

5
@Jessta:这就是我们的知名度和范围,对吧?
Thomas Ahle 2014年

6
goto引入新变量后,@ ThomasAhle Go不允许指向标签。执行“ goto”语句一定不能使任何变量在goto时尚未进入作用域。
km6zla 2014年

4
@ ogc-nick抱歉,我不清楚,我的意思是可以在需要它们的范围内声明这些函数,因此对于不需要它们的代码来说,它们是不可见的。我不是在谈论goto的范围。
Thomas Ahle 2014年

4
@MosheRevah引用的代码未针对可读性进行优化。通过在单个函数中跨越22行的goto,针对原始性能进行了优化。(而且托马斯·阿勒的提议在我眼中更具可读性。)
joel.neely

30

当没有内置控件功能可以完全满足您的要求时,并且可以使用goto表达想要的内容时,Goto是一个好主意。(在某些情况下,如果没有goto,在某些情况下会感到遗憾。您最终会滥用某些控制功能,使用布尔标志或使用比goto更糟糕的其他解决方案。)

如果某些其他控制功能(以相当明显的方式使用)可以完成您想要的操作,则应优先使用它而不是goto。如果不是,请大胆使用goto!

最后,值得注意的是,Go的goto具有一些限制,旨在避免某些难以理解的错误。请参阅规格中的这些限制


7

自从60年代和70年代的意大利面条式代码时代以来,Goto语句就受到了很多声名狼藉。那时,几乎没有软件开发方法。但是,Goto并不是天生的邪恶,但是当然可以被懒惰或不熟练的程序员滥用和滥用。滥用Gotos的许多问题可以通过开发过程来解决,例如团队代码审查。

goto在相同的技术方式的跳跃continuebreakreturn。有人可能会说,这些陈述以同样的方式是邪恶的,但事实并非如此。

Go团队之所以将Gotos包括在内,可能是因为它是常见的流控制原语。此外,他们希望得出结论,围棋的范围不包括使白痴安全的语言无法被滥用。


continue,,breakreturn在一个关键方面有很大不同:它们仅指定“离开封闭范围”。它们不仅鼓励而且明确要求开发人员考虑其代码结构,并依赖结构化的编程原语(用于循环,函数和switch语句)。goto语句唯一且唯一可以节省的地方是,它们允许您在编译器的优化器无法完成任务时使用HLL编写程序集,但这是以可读性和可维护性为代价的。
Parthian Shot

这是如此令人惊讶的去寻找原因在于,在golang开发商不得不结构化编程范式和“例外流量控制”之间进行选择任何其他情况下/指令指针操作一样setjmplongjmpgoto,和try / except / finally他们选择犯错就在身边小心点 goto首先,是默认的“结构化编程”控制流程。
Parthian Shot
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.