Perl 循环标签算作 GOTO 吗?
一般来说,避免 GOTO 是一种很好的做法。记住这一点,我一直在与一位同事就这个话题进行辩论。
考虑以下代码:
Line:
while( <> ) {
next Line if (insert logic);
}
使用循环标签算作转到吗?
以下是perlsyn 必须说:
以下是 C 程序员如何用 Perl 编写特定算法的代码:
for (my $i = 0; $i < @ary1; $i++) {
for (my $j = 0; $j < @ary2; $j++) {
if ($ary1[$i] > $ary2[$j]) {
last; # can't go to outer :-(
}
$ary1[$i] += $ary2[$j];
}
# this is where that last takes me
}
下面是更熟悉该习惯用法的 Perl 程序员可能会如何做到的:
OUTER: for my $wid (@ary1) {
INNER: for my $jet (@ary2) {
next OUTER if $wid > $jet;
$wid += $jet;
}
}
我对此的看法是否定的,因为你明确地告诉循环短路并前进,但我的同事不同意,他说这只是一个花哨的 GOTO,应该避免。 我正在寻找一个令人信服的论据或文档来解释为什么这是或不是 GOTO。我还将接受为什么这在 Perl 中被认为是或不是良好实践的解释。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
Dijkstra 的意图绝不是认为任何类似于 goto 的东西都被认为是有害的。使用 goto 作为几乎任何类型的程序流程更改的主要构造的代码结构将导致我们今天所说的意大利面条代码。
您应该阅读原始文章,并记住它写于 1968 年,当时标记跳转是几乎所有编程语言中的主要流程控制结构。
https://www.cs.utexas.edu/users/EWD/ ewd02xx/EWD215.PDF
Dijkstras intent was never that anything resembling goto is to be considered harmful. It was that the structure of code where gotos are used as the main construct for almost any kind of program flow change will result in what we today call spaghetti code.
You should read the original article and keep in mind that it was written in 1968 when labeled jumps was the main flow control constructs in just about all programming languages.
https://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF
GOTO 标签的危险在于它们会创建意大利面条式代码并使逻辑不可读。在这种情况下,这两种情况都不会发生。使用 GOTO 语句有很多有效性,大部分辩护来自 Donald Knuth [文章]。
深入研究 C 和 Perl 示例之间的差异...如果您考虑 C 程序在汇编级别发生的情况,无论如何它都会编译为 GOTO。如果您做过任何 MIPS 或其他汇编编程,那么您就会发现大多数这些语言没有任何循环结构,只有条件和无条件分支。
最终归结为可读性和可理解性。保持一致对这两者都有很大帮助。如果您的公司有风格指南,请遵循它,否则遵循 Perl 风格指南对我来说听起来是个好主意。这样,当其他 Perl 开发人员将来加入您的团队时,他们将能够立即投入使用并熟悉您的代码库。
The danger of GOTO labels is that they create spaghetti code and make the logic unreadable. Neither of those will happen in this case. There is a lot of validity in using GOTO statements, much of the defense coming from Donald Knuth [article].
Delving into the differences between your C and Perl example... If you consider what is happening at the assembly level with your C programs, it all compiles down to GOTOs anyway. And if you've done any MIPS or other assembly programming, then you've seen that most of those languages don't have any looping constructs, only conditional and unconditional branches.
In the end it comes down to readability and understandability. Both of which are helped an enormous amount by being consistent. If your company has a style guide, follow that, otherwise following the perl style guide sounds like a good idea to me. That way when other perl developers join your team in the future, they'll be able to hit the ground running and be comfortable with your code base.
只要它能让代码更容易理解,谁会关心它是否算作 goto 呢?使用 goto 通常比在 if() 和循环条件中进行大量额外测试更具可读性。
Who cares whether it counts as goto as long as it makes the code easier to understand? Using goto can often be MORE readable than having a bunch of extra tests in if() and loop conditions.
IMO,你的代码比较是不公平的。目标是可读的代码。
公平地说,您应该将惯用的带标签的 Perl 嵌套循环与不带标签的 Perl 嵌套循环进行比较。 C 风格的 for 和 block if 语句增加了噪音,使得无法比较这些方法。
标签:
没有标签:
我更喜欢带标签的版本,因为它明确说明了条件
$wid > 的效果。 $jet
。如果没有标签,您需要记住last
在内循环上运行,当内循环完成时,我们将移至外循环中的下一项。这并不完全是火箭科学,但它是真实的、可证明的认知开销。正确使用标签可以使代码更具可读性。更新:
stocherilac询问如果嵌套循环后有代码会发生什么。这取决于您是想根据内部条件跳过它还是始终执行它。
如果您想跳过外循环中的代码,标记的代码将按需要工作。
如果您想确保每次都执行它,可以使用
continue
块。IMO, your code comparison is unfair. The goal is readable code.
To be fair, you should compare an idiomatic Perl nested loop with labels against one without them. The C style for and blocked if statement add noise that make it impossible to compare the approaches.
Labels:
Without labels:
I prefer the labeled version because it is explicit about the effect of the condition
$wid > $jet
. Without labels you need to remember thatlast
operates on the inner loop and that when the inner loop is done, we move to the next item in the outer loop. This isn't exactly rocket-science, but it is real, demonstrable, cognitive overhead. Used correctly, labels make the code more readable.Update:
stocherilac asked what happens if you have code after the nested loop. It depends on whether you want to skip it based on the inner conditional or always execute it.
If you want to skip the code in the outer loop, the labeled code works as desired.
If you want to be sure it is executed every time, you can use a
continue
block.我认为区别有点模糊,但以下是 goto perldoc 关于 ( goto 语句:
goto-LABEL 形式找到标有 LABEL 的语句并在那里恢复执行。
...
Perl 的作者从未觉得需要使用这种形式的 goto (在 Perl 中,即;C 是另一回事)。 C 不提供与循环控制相结合的命名循环。Perl 提供,并且这取代了其他语言中 goto 的大多数结构化用法。)
(不同之处在于, 然而,org/perlsyn.html" rel="nofollow noreferrer">perlsyn perldoc 是这样说的:
只要表达式为 true,while 语句就会执行块。只要表达式为 false,until 语句就会执行块。 LABEL 是可选的,如果存在,则由一个标识符后跟一个冒号组成。 LABEL 标识循环控制语句 next、last 和 redo 的循环。如果省略 LABEL,则循环控制语句指最内层的封闭循环。这可能包括在运行时动态查看调用堆栈以查找标签。如果您使用 use warnings pragma 或 -w 标志,这种绝望的行为会触发警告。
绝望的行为位对我来说看起来不太好,但我可能会误解它的含义。
学习 Perl 书(第 5 版,第 162 页)这样说:
当您需要时要使用不是最内层的循环块,请使用标签。
...
请注意,标签命名了整个块;它没有在代码中标记目标点。 [这毕竟不是goto。]
这有助于澄清问题吗?可能不是...:-)
I think the distinction is somewhat fuzzy, but here's what the goto perldoc states about the (frowned upon) goto statement:
The goto-LABEL form finds the statement labeled with LABEL and resumes execution there.
...
The author of Perl has never felt the need to use this form of goto (in Perl, that is; C is another matter). (The difference is that C does not offer named loops combined with loop control. Perl does, and this replaces most structured uses of goto in other languages.)
The perlsyn perldoc, however, says this:
The while statement executes the block as long as the expression is true. The until statement executes the block as long as the expression is false. The LABEL is optional, and if present, consists of an identifier followed by a colon. The LABEL identifies the loop for the loop control statements next, last, and redo. If the LABEL is omitted, the loop control statement refers to the innermost enclosing loop. This may include dynamically looking back your call-stack at run time to find the LABEL. Such desperate behavior triggers a warning if you use the use warnings pragma or the -w flag.
The desperate behaviour bit doesn't look too good to me, but I may be misinterpreting its meaning.
The Learning Perl book (5th edition, page 162) has this to say:
When you need to work with a loop block that's not the innermost one, use a label.
...
Notice that the label names the entire block; it's not marking a target point in the code. [This isn't goto after all.]
Does that help clear things up? Probably not... :-)
Perl 中的标记循环跳转是 GOTO,就像 C 的
break
和continue
一样。Labeled loop jumps in Perl are GOTOs as much as C's
break
andcontinue
are.我会这样回答,我不确定这是否与其他人所说的有足够的不同:
因为你只能在当前作用域内移动,或者移动到父作用域,所以它们要少得多比
goto
通常暗示的危险,观察:这显然应该有效,但事实并非如此(不能朝这个方向移动)。
标签
的许多用例与 goto 不同,因为它们只是核心流程控制的更明确的变体。如果说它们绝对更糟糕,那就意味着:在某种程度上比
这更糟糕,如果你排除风格的话,这是完全不合逻辑的。但是,说到风格,后一种变体更容易阅读 - 并且它确实使您不必查找正确命名的标签。读者通过
next
了解更多(您正在当前作用域中重新启动循环),这样更好。让我们看另一个例子
-vs-
在后一个例子中,
next FOO
,跳过stuff
——您可能希望这样做,但这是个坏主意。这意味着程序员已经完整地读取了父作用域,这是最好避免的假设。总而言之,label
并不像goto
那么糟糕,有时它们还可以简化代码;但是,在大多数情况下应该避免它们。当我在 CPAN 上遇到循环时,我通常会重写没有标签的循环。I would answer it like this, and I'm not sure if this is sufficiently different from what others have said:
Because you can only only move inside of the current scope, or to a parent scope, they're much less dangerous than what is typically implied by
goto
, observe:This should work obviously, but this won't (can't move in this direction).
Many use cases of
labels
differ from goto in that they're just more explicit variants of core flow control. To make a statement that they're categorically worse would be to imply that:is somehow worse than
which is simply illogical if you exclude style. But, speaking of style, the latter variant is much easier to read - and it does stop you from having to look up for the properly named label. The reader knows more with
next
(that you're restarting the loop in the current scope), and that is better.Let's look at another example
-vs-
In the latter example here
next FOO
, skipsstuff
-- you might desire this, but it is bad idea. It implies that the programmer has read a parent scope to completion which is an assumption probably better avoided. In summary,label
isn't as bad asgoto
and sometimes they can simplify code; but, in most cases they should be avoided. I usually rewrite loops withoutlabel
s when I encounter them on CPAN.goto
很糟糕,因为它们创建了难以理解的代码——特别是通常被称为“意大利面条代码”的代码。下一行...
有什么难以理解的地方?您可以将其称为循环“名称”,它确实可以帮助强调循环边界。您不会跳入与循环相关的任意点;而是跳入与循环相关的任意点。您将返回到循环的顶部。
可悲的是,如果这是一个团体或内部标准,可能没有什么可以说服团体这不是一个首选。我有一位经理,他绝对坚持认为三元运算符会使事情变得难以阅读,并且更喜欢我对所有内容都使用 if 块。我有一个很好的论点,在 if-else 的子句中可以做任何事情,但是三元组明确表明您正在寻找特定的值。没有销售。
gotos
are bad because they create hard to understand code--particularly, what is often called "Spaghetti Code". What's hard to understand aboutnext Line...
??You can call it a loop "name", and it really is something to help emphasize loop boundaries. You're not jumping into an arbitrary point in relation to the loop; you're going back to the top of a loop.
Sadly enough, if it is a group or house standard, there might be nothing to convince the group that it's not a goto. I had a manager who absolutely insisted that a ternary operator made things hard to read, and preferred I use if-blocks for everything. I had a pretty good argument anything can be done in the clauses of an if-else, but that a ternary made it explicit that you were looking for a particular value. No sale.
这种跳转是类似 goto 语句的严格使用。因此,它肯定比不规范使用 goto 的危害要小。 (正如 kasperjj 所写,“Dijkstras 的意图绝不是认为任何类似于 goto 的东西都被认为是有害的。”)
IMO,这种 Perl 类型的跳转甚至比 C 的“break”和“continue”更好的设计,因为它清楚地表明了我们要中断或继续的循环,并且使它在面对代码更改时更加可靠。 (此外,它还允许中断或继续外循环。)
有些专家不喜欢中断/继续之类的东西,但在某些时候,需要在经验规则和可读性之间进行权衡,并且良好- 选择的中断/继续甚至转到可能会比“政治正确”代码更具可读性。
This kind of jump is a disciplined used of a goto-like statement. So it's certainly less harmful than undisciplined use of goto. (As kasperjj wrote, "Dijkstras intent was never that anything resembling goto is to be considered harmful.")
IMO, this Perl kind of jump is even better design than C's "break" and "continue", because it makes clear what loop we break or continue, and it makes it more solid in the face of code changes. (Besides, it also allows to break or continue an outer loop.)
There are pundits who don't like break/continue and the like, but at some point there is a tradeoff to make between rules of thumb and readability, and a well-chosen break/continue or even goto may become more readable than "politically correct" code.
中断/最后一个和继续/下一个是 goto。我不明白为什么有人会不遗余力地避免使用关键字,却使用不同的关键字来做同样的事情......
break/last and continue/next ARE gotos. I don't understand why anyone would go to such lengths to avoid a keyword yet use a different keyword that does the same thing...
4.4.4.循环控制
我们提到您可以在循环上放置一个标签来为其命名。循环的 LABEL 标识循环控制运算符 next、last 和 redo 的循环。 LABEL 将循环命名为整个循环,而不仅仅是循环的顶部。因此,引用循环的循环控制运算符实际上并不“转到”循环标签本身。就计算机而言,标签可以很容易地放置在循环的末端。但出于某种原因,人们喜欢在顶部贴上标签的东西。
Perl编程
4.4.4. Loop Control
We mentioned that you can put a LABEL on a loop to give it a name. The loop's LABEL identifies the loop for the loop-control operators next, last, and redo. The LABEL names the loop as a whole, not just the top of the loop. Hence, a loop-control operator referring to the loop doesn't actually "go to" the loop label itself. As far as the computer is concerned, the label could just as easily have been placed at the end of the loop. But people like things labeled at the top, for some reason.
Programming Perl