
因为有的人喜欢盲目goto
以下內容取材自 DEK 1970 年写的 Structured Programming with go to Statement. https://pic.plover.com/knuth-GOTO.pdf 他在文中表示文章标题是为 click bait 写的,不代表他强烈支持使用 go to。以下內容包括部分虛构。
首先讲个笑话,日本东大有个教授姓 Goto (如果是搞 Lisp 的应该会比较常看到他的名字,我一开始看到他名字想到的就是 go to),他曾亲自向 DEK 抱怨过自己被“扺制”的事。
至于是谁先开始抵制 go to 的。
我相信放今天反而是大多数人不知道带 go to 的八皇后要怎么写。
比如各种 exception 结构。
写了 go to Considered K 的 D[i][j][k] str *a 后来说:
然后,DEK 打了个比方,就是 Russell paradox 之后出现的 intuitionist logic 不能被广泛接受一事,之后虽然 intuitionist math 接受程度有所增加,主流还是在用改进后的 classic logic 和 excluded middle。
DEK 还大胆预言了 Haskell(或者说 Miranda?) 会在 1984 年后出现。并发出了神之拷问
https://hackage.haskell.org/package/GotoT-transformers-1.0.0.1/docs/Control-Monad-Trans-Goto.html
DEK 给的例子。
Program 1
For array A with m elements (index origin 1), and array B which B[i] keeps a count of the number of times above algorithm has been invoked with A[i]. Search A for value equal to x, if not found, add x to the end of A, and update B accordingly.
Without go to, it would be like Program 1a:
何切?
DEK 给的说法是,如果在 Program 1 的 for loop 里,在 if A[i] = x
之前加个 statement,Program 1a 改起来就麻煩了。
上面的程序太拿衣服了, 见 Program 2
作为一个常年 thinking in machine code 的 programmer,DEK 表示,这样做可以避免 for loop 里 i < m
的比较,有效提升 inner loop 速度。
你看,这不就体现 go to-less programming 的优越性了么?
DEK 说,nonono,真要是注重性能的话,我会这么写
这样用 double looping 可以減少循环总次数。
之后就是那句名言的出处,
希望这个 context 有帮助各位理解这句话吧。
不知道会不会因为 MMIX 改计划,我反正现在在学 PL360 了。
实际当中,DEK 最常用的 loop 形式是
如果 S 是空的话,这个形式就是 while loop,如果 T 是空的,就是 repeat loop 了。
用 structured ALGOL 写的话,有三种形式
1. S; while not B do begin T; S end;
2. undo T; repeat T; S until B;
3. repeat S; if not B then T fi; until B;
显然这三种形式都要重复代码。
ALGOL W 加入了 exit:
repeat begin S; when B exit; T; end;
BCPL 和 BLISS 用了 label:
Ole-Johan Dahl 是第一个提出能让 DEK 滿意的对应语法的人
loop S; while not B: T; repeat;
而且在 Hoare Logic 里可以这样定义
所有人都听过 structured programming,少有人去读过 EWD 写的那本书。我以为这是对用不用 go to 的爭论的结怔所在。
现在流行编程语言中,除了C语言的资源清理,我不知道还有什么情况下goto是合理的?
可能是因为没意识到自己用的是C语言吧
C++的话我绝对会100%禁止goto,虽然标准规定了很多情况下错误使用goto语句不能正确编译,但我并不能确信所有和生命周期有关的问题编译器都能解决,而且即便编译器处理正确,人也未必能正确理解编译器做了怎样的处理。C++要跳出控制流,最简单的方法就是用异常,而且可以跨函数使用,如果的确有很多错误处理相关的需要,肯定是直接用异常。其他情况下实在没办法写个do{}while(0);也比goto强一点。现在还有直接写个lambda然后return的方法。
纯C么,我就没见过几个水平适合写纯C的人,就不说了。
很多年前我对goto的态度也没有这么绝对,因为那个时候所有的代码都是我自己一个人写,我对自己比较自信,但我现在认为,任何时候都应当假设自己的代码将来需要由其他人来维护,还是把代码写成不管谁来都能简单改对的形式比较好。
goto 不是不能用(Linux 内核其实就使用了很多 goto,用的好的 goto 可以提升可读性),但是用好很难(因为这东西是直接跳转,可能会遇到未预期的行为)
此外,关于 goto,推荐阅读 Dijkstra 1968年的经典文章 Go To Statement Considered Harmful
https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf
没有goto的语言(面向过程或面向对象的语言)我不会去用。
从单层循环体之中跳出,一般使用break语句即可。
但是,如果在嵌套的两层循环之中,当需要从最内层循环体出发、跨越中间层循环体跳转到最外层,则使用goto语句会比“在两层循环体之中分别使用break语句”要方便得多。这应该算goto语句可以合理存在的价值之一吧。
可能主要原因在于goto语法过于跳脱,和 while 循环、for循环相比,阅读性及可维护性会差一点。
但是goto用于处理console的用户输入,可以接近脚本语言的写法,在这个场景中,我也会偶尔使用。除此之外,goto用的确实很少。
多数情况有更好的替代。曾经我认为跳出多层循环用goto或者labeled break会挺好使,后来发现助手函数加return更容易维护
乱用goto会导致代码逻辑混乱
内核里面的goto很多不仅不破坏代码的架构,反而成为了架构里很重要的一环,成为模块界限的缔造者。
我看到好多人用这个跳出多层循环的,坦白说建议进局部函数走return。
函数的机制就是一个加强版goto。
goto就是代码跳转,可以高效地汇总逻辑分发逻辑,没有必须用的地方或者绝对不可替代的好用之处。
而大多数人去使用goto的时候用意是为了省写一些什么东西,或者炫耀一下自己知道这个。这就很可怕了。这些个初衷下即使不是goto,换成任何其他语法都是烂代码。
所谓烂代码无法避免的话,那不如尽可能不要让它太烂,禁止goto的意义大概也就在于这里吧。
因为某个东西如果可能被滥用,那么在用户数量,质量不可控的情况下
一刀切是更省事的方法
多人协作的业务项目,不用goto 是对的,goto是多人协作的天敌。
单人项目,有些特殊工程,goto特别方便, 比如你写个正则引擎 或者 写个语言层协程,不用goto的话累的要死。
goto属于艺术家的语法: 大笔一挥,想去哪就去哪,无拘无束,当然也容易脱缰碰壁,晕头转向。
该用就用,不要怕。很多经典C代码写的parser里面goto满天飞。
如果你的代码能非常清晰,不言自明,你就可以盲目goto。
但你不能。