Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 解开Knuth';s结:如何重新构造意大利面代码?_C++_Algorithm_Loops_Refactoring_Software Design - Fatal编程技术网

C++ 解开Knuth';s结:如何重新构造意大利面代码?

C++ 解开Knuth';s结:如何重新构造意大利面代码?,c++,algorithm,loops,refactoring,software-design,C++,Algorithm,Loops,Refactoring,Software Design,这个问题的灵感来源于它询问如何从代码中通过算法消除goto语句。科学论文中描述了一般问题的解决方法 我根据Knuth的《计算机编程的艺术》(the art of computer programming)中的算法X的高级草图实现了一些代码,描述了使用受限前缀生成词典排列(参见本文第16页) 这是上述算法的对应部分 这可能是一个非常聪明和高效的算法,但代码的结构似乎很难遵循。我最终使用了良好的老式goto风格的实现: //Algorithm X; 1: initialize(); 2: enter

这个问题的灵感来源于它询问如何从代码中通过算法消除
goto
语句。科学论文中描述了一般问题的解决方法

我根据Knuth的《计算机编程的艺术》(the art of computer programming)中的算法X的高级草图实现了一些代码,描述了使用受限前缀生成词典排列(参见本文第16页)

这是上述算法的对应部分

这可能是一个非常聪明和高效的算法,但代码的结构似乎很难遵循。我最终使用了良好的老式
goto
风格的实现:

//Algorithm X;
1:
initialize();
2:
enter_level(k);
3:
set(a[k],q);
if(test() == ok) {
  if (k == n) {
    visit();
    goto 6;
  }
  goto 4;
}
goto 5;
4:
increase(k);
goto 2;
5:
increasev2(a[k]);
if (q != 0) {
  goto 3;
}
6:
decrease(k);
if (k==0) {
  goto 7;
}
set(p,u_k);
goto 5;
7:
return;
问题是:如何重构此代码以消除所有的
goto
调用

一个(伪造的)答案是建议“查阅引用的科学论文,逐行跟踪”——事实上,这当然是一种可能性。但这个问题是关于有经验的程序员一旦看一眼就会立即看到什么

我感兴趣的是如何一步一步地重构,而不仅仅是代码


注:

  • 基于算法X的高级规范和
    goto
    跳转,实际实现算法X非常简单。实现黑盒函数
    initialize()
    等只需要一些额外的指令,但这些指令与代码的结构无关。函数调用期间发生的事情并不重要,因为现在的重点是程序的流程
  • 关于“是”的惯常辩论与这个问题完全无关,根本不应在答复和评论中提及


  • 相关:

    您可以使用许多变量来模拟GoTo的流程,以使用
    if's
    while's

    initialize();
    
    enterLevel = true;
    executeWhile = true;
    
    do 
    {
    
        if (enterLevel)
        {
            enter_level(k);
        }
    
        enterLevel = false;
    
        goto4 = false;
        goto5 = false;
        goto6 = false;
    
        set(a[k],q);
        if(test() == ok) 
        {
            if (k == n) 
            {
                visit();
                goto6 = true;
            }
            else
            {
                goto4 = true;
            }
        }
        else
        {
            goto5 = true;
        }
    
        if (goto4) 
        {
            increase(k);
            enterLevel = true;
        }
        else
        {
            do
            {
                if(goto5)
                {
                    increasev2(a[k]);
                    goto6 = goto5 = !(q != 0); // if (q != 0) { goto6 = goto5 = false; } else { goto6 = goto5 = true; }
                }
                if(goto6)
                {
                    decrease(k);
                    executeWhile = !(k==0); // if (k == 0) { executeWhile = false; } else { executeWhile = true; }
                    set(p,u_k);
                    goto5 = true;
                }
            } while (goto5 && executeWhile);
        }
    } while (executeWhile);
    
    不过我不能说这个版本是否比带有
    goto的版本好


    首先,我将所有标签完全分开

    然后我发现这里有两个循环:

    1 - 
        * label 4 -> goto 2
        * label 5 -> goto 3. 
    
    两者都进入代码顶部,但其中一个执行
    enter_level(k)
    ,而另一个不执行。 这就是为什么enterLevel变量

    2 - 
        * label 6 -> goto 5. This goes up a little in the code, and then executes again. 
    
    在这个循环中,有两种情况下它会失效:

        * label 5 -> goto 3. The same as before, but now inside a nested loop
        * label 6 -> goto 7. The way out of the outer loop.
    
    其他变量和if只是为了维持控制流

    是的,我可以使用一些中断(代码可以变得更短), 但问题是关于goto's,我个人不喜欢使用它们。

    不需要太多努力(也不需要太多风险),您可以快速减少goto和标签的数量

    1) 删除未在任何位置引用的标签(这将是标签1:)

    2) 查找除了goto之外无法输入的代码块,这些代码块在很少的地方被调用。这些通常可以简单地分解出来。4:可以通过将代码移动到调用它的地方来处理,并且可以安全地完成,因为它唯一的出口是goto。这也允许我们删除它上面的goto 5,因为该代码只会下降到5:。7:可以通过修改if语句来处理。在这一点上,我们有

    initialize();
    2:
    enter_level(k);
    3:
    set(a[k],q);
    if(test() == ok) {
      if (k == n) {
        visit();
        goto 6;
      }
      increase(k);
      goto 2;
    }
    5:
    increasev2(a[k]);
    if (q != 0) {
      goto 3;
    }
    6:
    decrease(k);
    if (k!=0) {
      set(p,u_k);
      goto 5;
    }
    return;
    
    我倾向于到此为止。但如果您继续,就变成了识别循环并用循环构造替换GOTO的问题。然而,由于代码的结构方式,进行这些更改的风险似乎要大得多。此外,你可能会以休息和继续结束,这是一种无论如何都要做的事情。我的结论是(如果没有一些非常严格的测试,我不会保证它的正确性):

    我做了3:一个循环,6:一个内循环。我通过复制5:代码来代替goto,并用中断替换GoTo3,从而摆脱了GoTo5。这使得制作更干净的循环更容易一些。goto 6通过使用else进行修复。goto 3的功能正在继续

    在此之后(如果您还有剩余能量),您可以尝试将循环从while(true)with continues更改为whiles with实际条件


    首先开发测试,然后进行一两次更改和测试,这是一个好主意。再做一次更改,然后再次测试。如果你不这样做,很容易在早期就犯一个结构错误,然后使后续步骤无效,并迫使你从头再来。

    < P> C++中,该算法可以写成:

    void initialize() {}
    void enter_level(int k) {}
    void set(int x,int y) {}
    bool test() { return true; }
    void visit() {}
    void increase(int k) {}
    void increasev2(int k) {}
    void decrease(int k) {}
    
    void algorithm_x()
    {
        int k{0};
        int a[] ={1,2,3,4,5};
        int q{0};
        bool ok{true};
        int n{0};
        int p{0};
        int u_k{0};
    
            //Algorithm X;
        lbl1:
            initialize();
        lbl2:
            enter_level(k);
        lbl3:
            set(a[k],q);
            if (test() == ok) {
                if (k == n) {
                    visit();
                    goto lbl6;
                }
                goto lbl4;
            }
            goto lbl5;
        lbl4:
            increase(k);
            goto lbl2;
        lbl5:
            increasev2(a[k]);
            if (q != 0) {
                goto lbl3;
            }
        lbl6:
            decrease(k);
            if (k==0) {
                goto lbl7;
            }
            set(p,u_k);
            goto lbl5;
        lbl7:
            return;
    
    }
    
    int main()
    {
        algorithm_x();
        return 0;
    }
    
    假设我们不使用break语句,那么程序可能是:

    void initialize() {}
    void enter_level(int k) {}
    void set(int x,int y) {}
    bool test() { return true; }
    void visit() {}
    void increase(int k) {}
    void increasev2(int k) {}
    void decrease(int k) {}
    
    void algorithm_x()
    {
        int k{0};
        int a[] ={1,2,3,4,5};
        int q{0};
        bool ok{true};
        int n{0};
        int p{0};
        int u_k{0};
    
        bool skiptail{false};
    
        //Algorithm X;
        initialize();
        enter_level(k);
        while (true) {
    
            skiptail = false;
            set(a[k],q);
            if (test() == ok) {
                if (k == n) {
                    visit();
                    decrease(k);
                    if (k==0) {
                        return;
                    }
                    set(p,u_k);
                    while (true) {
                        increasev2(a[k]);
                        if (q != 0) {
                            //goto lbl3;
                            skiptail = true;
                        }
                        if (!skiptail) decrease(k);
                        if (!skiptail) if (k==0) {
                            return;
                        }
                        if (!skiptail) set(p,u_k);
                    }
                }
                if (!skiptail) increase(k);
                if (!skiptail) enter_level(k);
                //goto lbl3;
                skiptail = true;
            }
            if (!skiptail) while (true) {
                increasev2(a[k]);
                if (q != 0) {
                    //goto lbl3;
                    skiptail = true;
                }
                if (!skiptail) decrease(k);
                if (!skiptail) if (k==0) {
                    return;
                }
                if (!skiptail) set(p,u_k);
            }
            if (!skiptail) increase(k);
            if (!skiptail) enter_level(k);
            //goto lbl3;
            skiptail = true;
            if (!skiptail) while (true) {
                increasev2(a[k]);
                if (q != 0) {
                    //goto lbl3;
                    skiptail = true;
                }
                if (!skiptail) decrease(k);
                if (!skiptail) if (k==0) {
                    return;
                }
                if (!skiptail) set(p,u_k);
            }
        }
    
    }
    
    int main()
    {
        algorithm_x();
        return 0;
    }
    
    更改使用了以下算法:

  • 扔掉未使用的标签。删除
    lbl1

  • 如果标签以goto结尾,则在使用该块的任何位置替换该块。 删除
    lbl4
    lbl6
    lbl7

  • 如果标签返回自身,则在while中放置块(true)。 拆下底部的
    lbl5
    lbl5
    现在是独立的,可以在使用的地方更换)

  • 如果一个块是独立的,则在使用它的地方进行更换。 删除
    lbl5

  • 如果一个标签跟在另一个标签后面,则在块的末尾放置一个转到下一个标签,以便按照规则2进行替换。 删除
    lbl2
    (可以
    转到lbl3

  • 现在我们只剩下代码中最后一个标签的
    goto
    。用
    skiptail=true
    替换
    goto lbl3
    ,将剩余的块放在
    while(true)
    块中,并设置剩余语句以检查
    skiptail=false
    。 移除
    lbl3
    并替换为
    skiptail=false


  • 我从未使用过
    goto
    ,但这似乎是一个有趣的挑战,所以我尝试了自己的重构

    首先,检查代码,看看每个标签上有多少语句
    goto
    ing;记住这一点很重要,以避免犯错误。在您的示例中,没有任何结果是1,因此我们可以忽略它

    有时候,我发现了它
    void initialize() {}
    void enter_level(int k) {}
    void set(int x,int y) {}
    bool test() { return true; }
    void visit() {}
    void increase(int k) {}
    void increasev2(int k) {}
    void decrease(int k) {}
    
    void algorithm_x()
    {
        int k{0};
        int a[] ={1,2,3,4,5};
        int q{0};
        bool ok{true};
        int n{0};
        int p{0};
        int u_k{0};
    
        bool skiptail{false};
    
        //Algorithm X;
        initialize();
        enter_level(k);
        while (true) {
    
            skiptail = false;
            set(a[k],q);
            if (test() == ok) {
                if (k == n) {
                    visit();
                    decrease(k);
                    if (k==0) {
                        return;
                    }
                    set(p,u_k);
                    while (true) {
                        increasev2(a[k]);
                        if (q != 0) {
                            //goto lbl3;
                            skiptail = true;
                        }
                        if (!skiptail) decrease(k);
                        if (!skiptail) if (k==0) {
                            return;
                        }
                        if (!skiptail) set(p,u_k);
                    }
                }
                if (!skiptail) increase(k);
                if (!skiptail) enter_level(k);
                //goto lbl3;
                skiptail = true;
            }
            if (!skiptail) while (true) {
                increasev2(a[k]);
                if (q != 0) {
                    //goto lbl3;
                    skiptail = true;
                }
                if (!skiptail) decrease(k);
                if (!skiptail) if (k==0) {
                    return;
                }
                if (!skiptail) set(p,u_k);
            }
            if (!skiptail) increase(k);
            if (!skiptail) enter_level(k);
            //goto lbl3;
            skiptail = true;
            if (!skiptail) while (true) {
                increasev2(a[k]);
                if (q != 0) {
                    //goto lbl3;
                    skiptail = true;
                }
                if (!skiptail) decrease(k);
                if (!skiptail) if (k==0) {
                    return;
                }
                if (!skiptail) set(p,u_k);
            }
        }
    
    }
    
    int main()
    {
        algorithm_x();
        return 0;
    }
    
    1: initialize();
    reached4=false;
    do5 = false;
    while(true){
        if (reached4){
          4: increase(k);
        }
        2: enter_level(k);
        while(true){
          if(do5){
            5:
            increasev2(a[k]);
            if (q != 0) {
              do5 = false;//goto 3
            }
          }
          if(!do5){
            3:
            set(a[k],q);
            if(test() == ok) {
              if (k == n) {
                visit();//goto 6;
              }else{
                reached4 = true;
                break;//goto 4
              }
            }
          }
          6:
          decrease(k);
          if (k==0) {
            7: return;
          }
          set(p,u_k);
          do5 = true;
        }
    }
    
    //Algorithm X;
    1:
    initialize();
    2:
    while (true) {
       enter_level(k);
       3: 
       while (true) {
          set(a[k],q);
          if (test() == ok) {
             if (k != n) exit_while@3;
             visit();
             decrease(k); // replicate logic at 6 to avoid jumping into middle of 5 loop
             if (k==0) return;
             set(p,u_k);
          }
          5:
          while (true) {
             increasev2(a[k]);
             if (q != 0) continue_while@3;
             6:
             decrease(k);
             if (k==0) return;
             set(p,u_k);
          } // while(true)@5
      } // while(true)@3
      4:
      increase(k);
    } // while(true)@2