在IT技术中,是否有必须使用while/do-while而不是for的情况?

23

这是我和我的老师之间长期争论的一个问题。是否存在一种情况,for循环绝对不能用来替代while/do-while循环?换句话说,是否存在一种特定情况,在该情况下for循环无法取代while循环;while/do-while是否以任何方式与for不同?


5
不能?我想不出一个。不应该?有很多情况,特别是在循环开始之前无法确定迭代次数的情况下。 - StormeHawke
2
这可能会有用:http://en.wikipedia.org/wiki/For_loop#Equivalence_with_while_loops - Gowtham
3
我对点赞数量感到惊讶。出于好奇,你站在哪一边辩论的立场? - Cruncher
relevant - zzzzBov
循环?你根本不需要任何循环,你只需要使用if语句和递归。 - Raedwald
@Raedwald 当你有goto时,就不需要循环! - Loïc Faure-Lacroix
4个回答

37
不,不存在这种情况。每个“do-while”循环都可以用“while”循环来表示(在循环之前执行一次循环体),反之亦然。反过来,每个“while”循环也可以这样做。
while (X) {
    ...
}

可以写成。
for (; X;) {
    ...
}

即我们省略了初始化和递增语句。我们还可以通过正确放置初始化和递增语句将for循环转换为while循环。
简而言之,总是可以从一种循环变体转换为另外两种。 for循环只是让您能够限制循环控制变量的范围并在顶部执行任何递增操作的好处。不用说,在许多情况下,一个特定的循环变体比其他变体更有意义;每个变体都有其特定的用例。
还要注意的是,乐趣不仅止于循环:还可以将每个循环转换为递归函数,反之亦然(尽管在实践中可能存在限制;例如,一个工作正常的循环,在转换为递归函数时可能会产生堆栈溢出错误)。
“while”/“do-while”与“for”有什么区别吗? 没有。例如,以下两个片段的字节码是相同的:
int x = 0;
while (x < 10) {
    x++;
}

并且

int x = 0;
for (; x < 10;) {  // or: for (; x < 10; x++) {}
    x++;
}

都变成:

   0: iconst_0      
   1: istore_1      
   2: goto          8
   5: iinc          1, 1
   8: iload_1       
   9: bipush        10
  11: if_icmplt     5
  14: return 

评论中有关于for-each循环的讨论,认为它们可能与其他循环类型本质不同。这是绝对不正确的;for-each循环只是迭代器(或循环数组)周围纯粹的语法糖。每个for-each循环也可以转换为其他每种循环类型。以下是一个示例:

for (String s : l) {  // l is a list of strings
    System.out.println(s);
}

并且

String s;
Iterator<String> iter = l.iterator();  // l is a list of strings
while (iter.hasNext()) {
    s = iter.next();
    System.out.println(s);
}

两者都成为:

  24: invokeinterface #33,  1           // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
  29: astore_3      
  30: goto          50
  33: aload_3       
  34: invokeinterface #39,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
  39: checkcast     #19                 // class java/lang/String
  42: astore_2      
  43: getstatic     #45                 // Field java/lang/System.out:Ljava/io/PrintStream;
  46: aload_2       
  47: invokevirtual #51                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  50: aload_3       
  51: invokeinterface #57,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
  56: ifne          33

6
forwhile 循环只是在条件跳转周围添加的语法糖! - chrylis -cautiouslyoptimistic-
2
@Cruncher 不,分支是我熟悉的每个现实世界架构中的基本操作;它(可选地)进行比较,然后使用新目标覆盖程序计数器。在JVM中,它是if<cond>,而在x86中,它是J*操作码。循环是由编译器管理的跳转模板实现的。 - chrylis -cautiouslyoptimistic-
1
@user2174407 看看 javap 程序,特别是 -c 选项。 - chrylis -cautiouslyoptimistic-
7
如果你试图向老师证明它们在字节码级别上是等价的,那么请展示这个回答。但我想你的老师更可能会采取以下方式:“出于可读性的考虑,不要使用for (; checkIsTrue; ) { ... } ,而应该使用while (checkIsTrue) { ... }。” - Andrew Coonce
2
@PeterOlson 我已经描述了如何将 do-while 转换为 while,也描述了如何将 while 转换为 for。因此,我也描述了如何将 do-while 转换为 for。 - arshajii
显示剩余18条评论

6
不,你总是可以将 for 循环重写为 while 循环,将任何 while 循环重写为 for 循环。
<init>
while (condition) {
...
<increment>
}

等价于:

for (<init>; <condition>; <increment>) {
...
}

就价值而言,争论似乎在于是否总是可以使用for循环替代while循环,因此展示如何进行反向转换可能对OP更有用。 - Dennis Meng
2
@DennisMeng “等价于”在逻辑上是一个双条件(<==>),意味着它可以双向成立。否则,正确的措辞应该是“蕴含”,或者“如果这个有效,则这个也有效”。 - Cruncher
@Cruncher 我想更好的说法应该是“交换两者的顺序会使得对于OP与他的老师的辩论更易于理解”,而不是“展示如何转换...对于OP更有用”。 - Dennis Meng
如果<init>包含需要调用函数、声明多个不同变量或实例化类的几行代码,那该怎么办呢?我能想到的唯一限制是你可能无法在for循环的<init>部分放置任何想要的内容。(虽然你可以将该部分放在for循环之前并在内部省略它,但我不知道这是否算数 :)) - Spook
如果<init>需要多行代码,你需要将这些代码分组到一个函数中,并且只需调用该函数即可。 - Oleksi

2
其他答案已经涵盖了while循环和for循环之间的等价性。也就是说,
while(<expr>) {
  <body>
}

等同于

for(;<expr>;) {
}

请注意,使用do-while循环也可以实现类似的简化。任何do-while循环都可以。
do {
  <body>
} while(<expr>);

与...在功能上等价

for (boolean firstIter = true; firstIter || <expr>; firstIter = false) {
  <body>
}

+1. 除非firstIter已经存在并且在<body>和/或<expr>中被使用,否则此方法将失败。 - Joseph Quinsey
回到重点,这是一个不应该使用for循环的例子。显然,for和while是可以互换的,问题是在什么情况下使用哪个。 - Florian F

0

“绝对”?我会说不是。然而,在Java中,循环后的测试(即do-while)需要一个相当复杂的“for”条件。这就让人回到了布尔绝对值:条件必须评估为真或假。

因此,虽然我无法想象编译器不能被操纵以执行正确逻辑的情况,但我可以很快看出,任何维护您程序的人都可能希望你被石头砸死(或者认为你已经被砸死了)。

在相关的注释中,Java中没有什么是不能用机器语言完成的。但是,有很多非常好的理由不使用机器语言。大多数原因同样适用于当您尝试“聪明地”编写代码时。这一切都很有趣,直到您在凌晨3点接到愤怒的客户电话,或者您的老板打来电话。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接