是否可以始终消除goto的?

问题描述 投票:5回答:5

虽然使用goto轻松实现所有操作(如f.ex. IL所证明),但我想知道是否还可以消除具有更高级别表达式和语句的all goto语句-例如-使用Java支持的所有功能。 或者,如果您喜欢:我正在寻找的是“重写规则”,它将始终有效,无论创建goto的方式如何。

主要是出于理论上的问题,纯粹是出于兴趣;并非好/坏做法。

我考虑过的显而易见的解决方案是使用这样的东西:

while (true) { switch (state) { case [label]: // here's where all your goto's will be state = [label]; continue; default: // here's the rest of the program. } }

虽然这可能会起作用并且确实适合我的“正式”问题,但我一点也不喜欢我的解决方案。对于一个,它是丑陋的,对于两个,它基本上将goto包装到一个开关中,该开关的功能与goto完全相同。

所以,有更好的解决方案吗?


更新1

由于许多人似乎认为这个问题过于笼统,所以我将进一步阐述……我提到Java的原因是因为Java没有'goto'语句。作为我的业余爱好项目之一,我试图将C#代码转换为Java,事实证明这非常具有挑战性(部分是由于Java中的这一限制)。

这让我开始思考。如果您有外汇Open寻址中“删除”方法的实现(请参阅:http://en.wikipedia.org/wiki/Open_addressing-注1),在特殊情况下具有“转到”功能非常方便,尽管在这种特殊情况下,您可以通过引入“状态”来重写它'变量。请注意,这只是一个示例,我实现了连续的代码生成器,当您尝试对它们进行反编译时,它们会生成大量的goto。

我也不确定在此问题上的重写是否总是会消除'goto'语句,以及在每种情况下是否都允许使用该语句。尽管我不是要寻求正式的“证明”,但一些证据表明,在该问题上有可能消除。

关于“广泛性”,我向所有认为“答案太多”或“重写goto的方法很多”的人提出挑战,请他们提供重写一般情况的算法或方法,因为唯一的答案我到目前为止发现的是我发布的那个。

虽然使用goto实现一切都很容易(如f.ex. IL所证明的,但我想知道是否也可以消除所有具有更高级别的表达式和语句的goto语句-例如-...

] >> < [

此1994年论文:Taming Control Flow: A Structured Approach to Eliminating Goto Statements提出了一种用于消除C程序中所有goto语句的算法。该方法适用于用C#编写的任何程序或使用诸如if / switch / loop / break / continue(AFAIK,但我不明白为什么不这样做)之类的通用结构的任何语言。
以两个最简单的转换开始:

    案例1
  • Stuff1(); if (cond) goto Label; Stuff2(); Label: Stuff3();

成为:

Stuff1();
if (!cond)
{
  Stuff2();
}
Stuff3();

案例2
  • Stuff1(); Label: Stuff2(); Stuff3(); if (cond) goto Label;

    成为:

    Stuff1();
    do
    {
      Stuff2();
      Stuff3();
    } while (cond);
    
  • 并以此为基础来检查每个复杂的案例并应用导致这些琐碎案例的迭代转换。然后以最终的gotos / labels根除算法结束。

    这是一个非常有趣的阅读。

    UPDATE:关于该主题的其他一些有趣的论文(不容易掌握,因此在此复制直接链接以供参考):

    A Formal Basis for Removing Goto Statements

    A Goto-Elimination Method And Its Implementation For The McCat C Compiler

    我具有尝试采用非结构化程序(在COBOL中同样如此)并通过删除每个GOTO实例使其呈现结构化的实践经验。最初的程序员是一位经过改革的Assembly程序员,尽管他可能知道PERFORM语句,但是他没有使用它。是GOTO GOTO GOTO。这是严重的意大利面条代码-数百行意大利面条代码。我花了几个星期的空闲时间来尝试重写这个怪异的结构,最终我不得不放弃了。那是一大堆精神错乱的东西。它奏效了!它的工作是解析以文本格式发送到大型机的用户指令,并且做得很好。
    所以,不,如果您使用手动方法,则并非总是可能完全消除GOTO。但是,这是一个极端的情况-现有的代码是由一个显然具有扭曲的编程思想的人编写的。在现代,有可用的工具可以解决以前棘手的结构问题。

    从那天开始,我就使用Modula-2,C,Revelation Basic,三种VB和C#进行编码,但从未发现需要甚至建议使用GOTO作为解决方案的情况。但是,对于原始的BASIC,GOTO是不可避免的。

    可以避免使用goto的情况,但我认为最好使用它:
    当我需要退出内部循环和循环时:

    for(int i = 0; i < list.Count; i++) { // some code that initializes inner for(int j = 0; j < inner.Count; j++) { // Some code. if (condition) goto Finished; } } Finished: // Some more code.

    为了避免转到,您应该执行以下操作:

    for(int i = 0; i < list.Count; i++)
    {
        // some code that initializes inner
        bool conditon = false;
        for(int j = 0; j < inner.Count; j++)
        {
           // Some code that might change condition
           if (condition) break;
        }
        if (condition) break;
    }
    // Some more code.
    

    我认为goto语句看起来要好得多。

    如果内部循环使用其他方法,则第二种情况还可以。

    void OuterLoop(list) { for(int i = 0; i < list.Count; i++) { // some code that initializes inner if (InnerLoop(inner)) break; } } bool InnerLoop(inner) { for(int j = 0; j < inner.Count; j++) { // Some code that might change condition if (condition) return true; } return false; }

    由于您说的是理论问题,所以这里是理论答案。
    Java图灵当然是完整的,是的。您可以用Java表达任何C#程序。您也可以在Minecraft Redstone或Minesweeper中表达它。与这些替代方法相比,用Java表示它应该很容易。

    但是,帕特里斯给出了一个显然更实用的答案,即可以理解的算法来进行转换。

    我一点都不喜欢我的解决方案。对于一个,它是丑陋的,对于两个,它基本上将goto包装到一个开关中,该开关的功能与goto完全相同。
    这是寻找可以替代goto用法的通用模式时总会得到的结果,它的功能与goto完全相同。还有什么呢? goto的不同用法应替换为最佳匹配的语言构造。这就是为什么首先要有构造为switch语句和for循环的构造,以使创建这样的程序流更容易且更不易出错。编译器仍然会生成goto的(或跳转),但在我们搞砸的地方会始终如一地进行。最重要的是,我们不必阅读编译器生成的内容,而是可以阅读(并编写)更容易理解的内容。

    [您会发现,存在大多数编译器构造以概括goto的特定用法,这些构造是根据之前存在的常见goto使用模式创建的。 Patrice Gahide的论文提到了某种相反的过程。如果在代码中找到goto,则可以查看其中一种模式,在这种情况下,应将其替换为匹配的语言结构。或者,您正在查看本质上是非结构化的代码,在这种情况下,您应该实际构造代码(或不理会它)。将其更改为非结构化但没有goto的内容只会使情况变得更糟。 (顺便说一下,相同的泛化过程仍在进行,请考虑如何将foreach添加到编译器中以泛化for的非常常见的用法...)

    c# goto code-elimination
    5个回答
    10
    投票
    以两个最简单的转换开始:

    2
    投票
    所以,不,如果您使用手动方法,则并非总是可能完全消除GOTO。但是,这是一个极端的情况-现有的代码是由一个显然具有扭曲的编程思想的人编写的。在现代,有可用的工具可以解决以前棘手的结构问题。

    从那天开始,我就使用Modula-2,C,Revelation Basic,三种VB和C#进行编码,但从未发现需要甚至建议使用GOTO作为解决方案的情况。但是,对于原始的BASIC,GOTO是不可避免的。


    2
    投票
    当我需要退出内部循环和循环时:

    for(int i = 0; i < list.Count; i++) { // some code that initializes inner for(int j = 0; j < inner.Count; j++) { // Some code. if (condition) goto Finished; } } Finished: // Some more code.


    1
    投票
    Java图灵当然是完整的,是的。您可以用Java表达任何C#程序。您也可以在Minecraft Redstone或Minesweeper中表达它。与这些替代方法相比,用Java表示它应该很容易。

    但是,帕特里斯给出了一个显然更实用的答案,即可以理解的算法来进行转换。


    1
    投票
    我一点都不喜欢我的解决方案。对于一个,它是丑陋的,对于两个,它基本上将goto包装到一个开关中,该开关的功能与goto完全相同。
    这是寻找可以替代goto用法的通用模式时总会得到的结果,它的功能与goto完全相同。还有什么呢? goto的不同用法应替换为最佳匹配的语言构造。这就是为什么首先要有构造为switch语句和for循环的构造,以使创建这样的程序流更容易且更不易出错。编译器仍然会生成goto的(或跳转),但在我们搞砸的地方会始终如一地进行。最重要的是,我们不必阅读编译器生成的内容,而是可以阅读(并编写)更容易理解的内容。
    © www.soinside.com 2019 - 2024. All rights reserved.