【问题标题】:replace if/else with while/if statement用 while/if 语句替换 if/else
【发布时间】:2014-11-29 11:01:12
【问题描述】:

是否可以用whileif 语句替换if/else 语句(例如,我们正在使用C 语言)?如果可能的话,你能分享一个例子吗?所以,说我有

if (cond1)
    exec1
else 
    exec2

我真的很想摆脱 if/else 并只使用 if/while 构造。

图灵完备的语言有while/if 语句(用于控制流)就足够了吗?

我将如何使用 if/else 构造来做到这一点?

(这不是作业,这是出于好奇)

【问题讨论】:

  • 除非这是用于一些代码高尔夫,否则我不明白您为什么要这样做。此外,我看不到使用某种 while 循环构造来实现 else 分支的方法。 if-then 分支是,但不是 else。
  • if-else 可以替换为 switch-case 和条件运算符。

标签: c if-statement while-loop


【解决方案1】:

这有点笨拙,但假设任一分支的执行都不会改变条件,您可以用几个 while 替换 if-else 构造: 假设原文是:

if (cond) {
   exec1();
else {
   exec2();
}

只能替换为ifs:

if (cond) {
    exec1();
}
if (!cond) {
    exec2();
}

或者whiles:

while (cond) {
    exec1();
    break;
}
while (!cond) {
    exec2();
    break;
}

【讨论】:

    【解决方案2】:

    对于if() ... else ... 构造的一般替换,您可以缓存条件的结果:

    int condition = cond1;
    if(condition) exec1;
    if(!condition) exec2;
    

    这样可以避免cond1exec1 中的副作用问题。

    关于图灵完整性的问题:
    正如 Paul Griffiths 所说,if()goto 就足够了。但是,if() 和递归也是如此。您可以使用自递归函数替换任何 while(cond1) exec1; 循环:

    void loopy(/*whatever state the loop touches*/) {
        if(cond1) {
            exec1;
            loopy(/*pass on the current state*/);
        }
    }
    

    这一事实在 lisp 和 scheme 等函数式语言中被严重滥用。当您学习使用这些语言进行编程时,您会被教导以这样一种方式(尾递归)编写递归,以便编译器可以确定您打算编写一个循环并进行相应的优化......

    【讨论】:

      【解决方案3】:

      您甚至不需要while,您只需要能够比较并能够分支,换句话说,ifgoto。在下面的程序中,函数test_normally()test_subnormally() 是等价的:

      #include <stdio.h>
      
      void exec1(void)
      {
          puts("exec1() called");
      }
      
      void exec2(void)
      {
          puts("exec2() called");
      }
      
      void exec3(void)
      {
          puts("exec3() called");
      }
      
      void test_normally(void)
      {
          int cond1 = 0;
          int cond2 = 1;
          int i = 5;
      
          /*  First if test  */
      
          if ( cond1 )
              exec1();
          else
              exec2();
      
          puts("First if test over.");
      
          /*  Second if test  */
      
          if ( cond2 )
              exec1();
          else
              exec2();
      
          puts("Second if test over.");
      
          /*  While test  */
      
          while ( i > 0 ) {
              exec3();
              --i;
          }
      
          puts("Loop test over.");
      }
      
      void test_subnormally(void)
      {
          int cond1 = 0;
          int cond2 = 1;
          int i = 5;
      
          /*  First if test  */
      
          if ( !cond1 )
              goto cond1_false;
      
          exec1();
          goto cond1_end;
      
      cond1_false:
          exec2();
      
      cond1_end:
          puts("First if test over.");
      
          /*  Second if test  */
      
          if ( !cond2 )
              goto cond2_false;
      
          exec1();
          goto cond2_end;
      
      cond2_false:
          exec2();
      
      cond2_end:
          puts("Second if test over.");
      
          /*  While test  */
      
      loop_start:
          if ( !(i > 0) )
              goto loop_end;
      
          exec3();
          --i;
          goto loop_start;
      
      loop_end:
          puts("Loop test over.");
      }
      
      int main(void)
      {
          test_normally();
          putchar('\n');
          test_subnormally();
          return 0;
      }
      

      和输出:

      paul@local:~/src/sandbox$ ./goto
      exec2() called
      First if test over.
      exec1() called
      Second if test over.
      exec3() called
      exec3() called
      exec3() called
      exec3() called
      exec3() called
      Loop test over.
      
      exec2() called
      First if test over.
      exec1() called
      Second if test over.
      exec3() called
      exec3() called
      exec3() called
      exec3() called
      exec3() called
      Loop test over.
      paul@local:~/src/sandbox$ 
      

      通过比较这两个函数,希望你能明白为什么ifwhile 以及他们所有的朋友都比替代品更好。

      test_subnormally() 实际上非常接近处理器的实际工作,在您的 C 源代码编译成机器代码之后。这是在 64 位 Intel 处理器上来自 gcc 的 test_normally() 的汇编输出 - 您可以看到汇编指令和另一个函数 test_subnormally() 的 C 源代码之间几乎存在一对一的映射:

      .LC3:
          .string "First if test over."
      .LC4:
          .string "Second if test over."
      .LC5:
          .string "Loop test over."
          .text
          .globl  test_normally
          .type   test_normally, @function
      test_normally:
      .LFB3:
          .cfi_startproc          # // Function entry
          pushq   %rbp
          .cfi_def_cfa_offset 16
          .cfi_offset 6, -16
          movq    %rsp, %rbp
          .cfi_def_cfa_register 6
          subq    $16, %rsp
          movl    $0, -8(%rbp)    # // -8(%rbp) is cond1
          movl    $1, -12(%rbp)   # // -12(%rbp) is cond2
          movl    $5, -4(%rbp)    # // -4(%rbp) is i
          cmpl    $0, -8(%rbp)    # if ( !cond1 )    
          je  .L5                 # goto cond1_false;
          call    exec1           # exec1();
          jmp .L6                 # goto cond1_end;
      .L5:                        # cond1_false:
          call    exec2           # exec2();
      .L6:                        # cond1_end:
          movl    $.LC3, %edi     # // move "First if test over" to %edi
          call    puts            # puts("First if test over");
          cmpl    $0, -12(%rbp)   # if ( !cond2 )
          je  .L7                 # goto cond2_false;
          call    exec1           # exec1();
          jmp .L8                 # goto cond2_end;
      .L7:                        # cond2_false:
          call    exec2           # exec2();
      .L8:                        # cond2_end:
          movl    $.LC4, %edi     # // move "Second if test over" to %edi
          call    puts            # puts("Second if test over");
          jmp .L9                 # goto loop_start;
      .L10:                       # loop_body:
          call    exec3           # exec3();
          subl    $1, -4(%rbp)    # --i;
      .L9:                        # loop_start:
          cmpl    $0, -4(%rbp)    # if ( !(i > 0) ) ...
          jg  .L10                # ...goto loop_body;
          movl    $.LC5, %edi     # // move "Loop test over" to %edi
          call    puts            # puts("Loop test over");
          leave                   # // Function exit
          .cfi_def_cfa 7, 8
          ret
          .cfi_endproc
      

      编译器在这里选择了以稍微不同的顺序放置循环片段,但除此之外,它的读取几乎与 test_subnormally() 的 C 源代码完全相同。我们拥有ifwhile 以及他们在C 语言中的朋友的根本原因正是为了让我们不必编写看起来像这样的代码,但这种简单程度和类似意大利面条的代码才是处理器最终的目标最终执行(并且是图灵完备所需要的全部),因此我们有一个编译器,可以将人类看起来更易于理解和维护的东西变成处理器非常满意的那种混乱。

      【讨论】:

        【解决方案4】:

        如果cond1 未在exec1 中更改,则这些是等效的。

        if (cond1) exec1 else exec2
        
        if (cond1) exec1
        if (!cond1) exec2
        

        不需要。

        【讨论】:

          【解决方案5】:

          if、else 可以替换为: 使用 如果

          if(条件 cond1 为真) {

          执行exec1;

          }

          if (!(条件 cond1 为真)) //条件为假

          { 执行exec2;

          }

          使用 While 循环:

          while(条件 cond1 为真) {

          执行exec1;

          }

          while (!(condition cond1 is true)) //条件为假

          { 执行exec2;

          }

          【讨论】:

          • 你忘了休息,否则你的时间不会停止
          • 不,每次迭代都检查条件,同时在 while 循环内修改条件。例如迭代器++;
          猜你喜欢
          • 2019-04-09
          • 1970-01-01
          • 1970-01-01
          • 2016-08-03
          • 1970-01-01
          • 2015-03-18
          • 1970-01-01
          • 2015-11-08
          • 1970-01-01
          相关资源
          最近更新 更多