【问题标题】:Why is this loop bad practice? [closed]为什么这个循环是不好的做法? [关闭]
【发布时间】:2014-02-01 11:50:15
【问题描述】:

以下循环不是好的做法。是因为Stringfor 循环的主要条件而不是int 变量,这意味着for 循环是无限的吗?还有,是不是因为没有实例进入'end'来停止循环?

Scanner in = new Scanner(System.in);
int i = 0;
for (String s = in.next(); !s.equals("end"); i++) 
{
    System.out.println("The value of i is: " + i + " and you entered " + s);
}

如何重写它,使其符合公认的风格?

(这是过去试卷中的一个问题。)

【问题讨论】:

  • 这并不像无限循环那么糟糕......
  • “接受的风格”是指任何特定的标准集?
  • 我会说所述问题没有答案,因为无法将给定状态下的代码作为解决方案进行分析。这不是正确的代码。解决方案是否是“良好做法”取决于您实际拥有可行的解决方案时的外观。
  • 我为那些不得不处理设计不佳的问题和作业的学生感到抱歉。

标签: java loops coding-style


【解决方案1】:

这不是一个好主意,因为字符串s 可能永远不会等于"end"。您可能想检查扫描仪是否有另一个字符串。此外,您只需将字符串初始化为in.next(),但您需要在每次循环迭代后将s 设置为下一个字符串。

while(in.hasNext()) {
  String s = in.next();
  if (s.equals("end")) {
    break;
  }
  // ..
}

【讨论】:

    【解决方案2】:

    好吧,你的字符串s 永远不会改变,这会导致无限循环。你可能想要:

    for (String s = in.next(); !s.equals("end"); s = in.next(), i++) {
        ...
    }
    

    有些人(包括我)可能会说 i++ 不应该在这个循环的增量部分,因为它与条件没有直接关系:

    for (String s = in.next(); !s.equals("end"); s = in.next()) {
        ...
        i++;
    }
    

    是不是因为字符串是for循环的主要条件而不是int变量,意味着for循环是无限的?

    原始循环确实是无限的(至少,在输入初始输入并假设 "end" 不是第一个输入之后)。但是,这不是您所说的原因。 For 循环最常使用积分循环控制变量编写,但并非总是如此。例如,遍历链表的常见习语是:

    for (Node node = list.head; node != null; node = node.next) {
        ...
    }
    

    您的循环的问题在于字符串 s 永远不会更改,因此它永远不会等于 "end",除非这是第一个输入。

    【讨论】:

    • 即使这样,您也需要确保无论它循环到什么地方,总会在某个时候将“end”作为一个值,否则它将无限循环。
    • for (String s; !(s = in.next()).equals("end");) 消除了重复。
    • Marko:而且还取消了可读性……
    【解决方案3】:
    for (int i = 0; in.hasNext(); i++) {
        String s = in.next();
        if (s.equals("end")) {
            break;
        }
        ...
    

    无限循环,或无循环(当 s 最初是“结束”时)。

    【讨论】:

    • 这在while 循环中看起来会更干净
    • 我仍然认为没有理由使用 for 循环而不是 while 循环。您是否同意并且刚刚重写了 for 循环以使其正常工作?
    • 我这样做是为了限制i 的范围。一段时间似乎确实更有规律,因为部分并不完全连贯。
    【解决方案4】:

    for 循环的常见做法是计数器变量在每个术语中重复:

    for(int i=...; i<... ; i++)
    

    在上面的示例中,代码混合了变量。这让读者感到困惑,并且可能导致循环仅在您输入end 作为第一个值时才会终止的错误。

    【讨论】:

      【解决方案5】:

      这是不好的做法,因为它只有在下一个获得的令牌是“结束”时才会终止。它不考虑情况。例如输入流结束。

      因此,当流结束并且“结束”处无处出现时,您将在s.equals("end") 获得s=nullNullPointerException

      您可以更正它,例如通过将条件更改为in.hasNext() &amp;&amp; !"end".equals(s)

      而且 s 在初始化后就永远不会改变。

      【讨论】:

        【解决方案6】:

        我建议将循环条件和对Scannner.next()的调用分开:

        while (in.hasNext()) {
            String s = in.next();
            if (s.equals("end")) {
              break;
            }
            System.out.println("The value of i is: " + i + " and you entered " + s);
            i++;
        }
        

        我认为这比试图将所有内容都压缩到 for 表达式中更容易理解。

        【讨论】:

        • 我会支持这个。 while 循环对于迭代长度未知的循环的更好做法。
        • 是的,这比试图将所有逻辑塞进一个布尔表达式的解决方案要清晰得多。
        • @peeskillet 我不能说我同意这一点。是的,for 循环通常确实具有已知的迭代长度,但如果使用正确,使用一个并不一定是不好的做法。实际上,这里的 OP for 循环的一个有效用例:一个清晰的初始化、一个相关的条件和一个相关的增量步骤。事实上,还有其他常见的情况,其中 for 循环没有已知的迭代长度。我在回答中简要提到了这一点。
        【解决方案7】:

        这个循环是个坏主意,因为您从用户输入中设置s 一次,而不是在每次迭代中。 因此,如果s 填充的值与“end”不同。

        你可能想要更像这样的东西:

        for (String s; (s = in.nextLine()).equals("end"); i++) 
        {
            System.out.println("The value of i is: " + i + " and you entered " + s);
        }
        

        【讨论】:

          【解决方案8】:

          如果问题是“为什么要重写它”,答案基本上就像其他人指出的那样,它目前是一个无限循环,而且它的可读性也不是很好。就我个人而言,我会将它重写为一个 while 循环,其他几个人已经指出了如何去做,因为它使你的意图比一个带有无穷大计数器的 for 循环更清楚一些。不熟悉代码应该如何工作的人很容易将无限增量混淆为编写它的程序员的疏忽。

          【讨论】:

            【解决方案9】:

            字符串 s 永远不会被修改。循环永远不会结束。这个呢:

                Scanner in = new Scanner(System.in);
                String s = "";
                for (int i = 0 ; !s.equals("end"); i++) {
                    s = in.next();
                    System.out.println("The value of i is: " + i + " and you entered "
                            + s);
                }
            

            【讨论】:

              【解决方案10】:

              这种方法太糟糕了。

              给定的代码:-

              Scanner in = new Scanner(System.in);
              int i = 0;
              for (String s = in.next(); !s.equals("end"); i++) 
              {
                  System.out.println("The value of i is: " + i + " and you entered " + s);
              }
              

              for循环的第一部分一生只执行一次。

              String s = in.next() //Execute only once in life
              

              这个 for 循环的第二部分永远不会是 true ,因为输入控制台永远不允许输入第二个输入。

              !s.equals("end")//2nd part
              

              这个程序永远不会允许从控制台输入第二个输入,因为in.next() 只会执行一次。并且这个循环的退出标记是“结束”,在第一次输入后无法进入。

              这种类型的循环应该由while循环来实现。

              Scanner in = new Scanner(System.in);
              while(in.hasNext()){
              String yourdata=in.next();
              if(yourdata.equals("end")){
              //Stop the loop 
              }
              //Do you code here
              }
              

              【讨论】:

                【解决方案11】:

                这段代码存在多个问题:

                1. s 在初始分配后永远不会改变,所以它是一个无限循环。
                2. 调用.next() 可能会抛出NoSuchElementExceptionIllegalStateException。与其捕获这些异常,我认为事先检查.hasNext() 更为礼貌,因为输入用完是可预见的而不是异常情况。不过,另一种请求宽恕的方式也可以接受。
                3. for-loop 标头没有形成一个连贯的故事 - 它初始化 s 并测试 s,但更新 i
                4. 在我看来,System.out.format() 会比 System.out.println() 更可取。

                我会这样写:

                Scanner in = new Scanner(System.in);
                int i = 0;
                String s;
                while (in.hasNext() && !"end".equals(s = in.next())) {
                    System.out.format("The value of i is: %d and you entered %s\n", i++, s);
                }
                

                告诉用户end 是一个终止循环的魔法词(假设它被修改为可能按预期工作),这也可能是一个很好的用户界面触摸。

                【讨论】:

                • while 条件不是很可读。最好保留为while(in.hasNext()),其余的逻辑保留在循环体中。
                • @Petter 我觉得它足够可读。我更喜欢@andersschuller 的解决方案,while (in.hasNext()) 只讲述了部分故事。在这里,条件准确地描述了循环应该如何终止。
                【解决方案12】:

                其他人提到循环不会结束,因为你没有改变 s 的值,所以循环永远不会结束。这可能是你的教授想要的,也可能不是。通常,糟糕的代码是一种糟糕的做法,但还有其他原因导致这种做法成为一种糟糕的做法。

                我觉得这里的做法不好,教授的本意是在这里使用for 循环。正如我的教授告诉我的那样,“For 循环是在你知道代码何时结束时使用的,而循环是在你不知道代码何时结束时使用的。”所以如果你有一个可迭代的i,比如这个代码:

                for(i = 0; i<100; i++)
                {
                    ...
                }
                

                在这段代码中,您知道您想要将 i 从 0 迭代到 100。while 循环是您希望在教​​授讨论的情况下使用的。

                int counter;
                while(*(str+counter))
                    counter++;
                

                你不知道循环什么时候结束,因为你不知道str 有多长,但你知道它有时会到达空指针,循环就会终止。这通常是最佳实践。

                因此,对于您的教授发布的代码,您可能希望它看起来像这样:

                Scanner in = new Scanner(System.in);
                int i = 0;
                while(!s.equals("end"))
                {
                    i++;
                    String s = in.next();
                    System.out.println("The value of i is: " + i + " and you entered " + s);
                }
                

                【讨论】:

                • “正如我的教授告诉我的”——在 Java 这样的语言中,你的教授在客观上是错误的。
                • @KonradRudolph 有没有与之相关的解释?
                • 我认为从上下文来看这是显而易见的:它与既定的最佳实践相矛盾。在某些情况下使用for 循环,您无法静态地证明绑定是Java 中已建立的习惯用法。而且由于反对这样做的唯一论据是约定,所以这个论点不成立。
                • @KonradRudolph 我猜他们告诉我 C 语言,它也反映在我的 Python 书中。在 C 或 python 之前学习 Java,对我来说,逻辑也适用于 Java 是有道理的。这个线程中的许多其他人都在说同样的事情,while 循环会更适合这个。您在谈论哪些情况下无法静态证明界限?
                • 但对于 C 语言来说也是如此——事实上,这就是 Java 养成习惯的地方。 while 循环可能更适合在这种特殊情况下。这与你所说的不同(因此我没有看到其他人同意你的观点)。 for (Iterator i = something.iterator(); i.hasNext();) 是一种惯用用法,for (Scanner in = new Scanner(System.in); in.hasNext();) 也是如此。 Python 是完全不同的情况,因为 Python 的 for 循环功能不同。
                【解决方案13】:

                我认为这是不好的做法,因为不需要 for 循环。在这种情况下,我相信它是没有用的。可能就是这样:

                Scanner in = new Scanner(System.in);
                String s = in.next();
                if (!s.equals("end"))
                {
                    System.out.println("You have enetered" + s);
                }
                

                看,不需要循环。你的循环让事情变得比他们必须的更复杂。我一直认为事情应该尽可能简单,除非它们需要复杂性。 For 循环仅在您希望代码执行多个操作时使用。在上面的例子中,只发生了一件事:println 语句,因此不需要循环。没必要……

                此外,循环永远不会结束。所以也有,但这只是错误的代码。这不是为什么这是不好的做法。这是不好的做法,因为不必要地使用了 for 循环。这也是错误的,因为代码是错误的。所以这段代码有两个不同的地方。

                【讨论】:

                • 是的,需要一个循环。它应该重复接受输入并打印它直到输入是“结束”。
                【解决方案14】:

                上面的一些回答正确地说你写的是一个无限循环。但我想澄清为什么这是一个无限循环。您使用的 for 循环与您可能想到的其他形式不同:

                String[] stringArray = { "1", "2", "3" };
                for (String s : stringArray) {
                   System.out.println(s);
                }
                

                在这种情况下,变量 s 会在每次迭代时使用集合或数组中的下一个值进行初始化。但是这种形式的 for 循环适用于集合和数组,不能与 Scanner 类等迭代器一起使用。

                您使用的 for 循环形式的不同之处在于初始化子句(您有String s = in.next())在第一次通过循环时被调用ONLYs 是第一次设置的,然后就再也没有改变过。

                你可以这样重写:

                int i = 0;
                for (String s = in.next(); !s.equals("end"); s = in.next()) {
                    System.out.println("The value of i is: " + i++ + " and you entered " + s);
                }
                

                但这里的另一个坏事是没有空值或结束检查。可以想象,如果您不太可能在找到等于“end”的字符串之前用完字符串。如果发生这种情况,那么 for 测试子句(中间那个)会在尝试调用 equals() 时给你一个 NullPointerException > 方法。这绝对是不好的做法。我可能会这样重写:

                int i = 0;
                while (in.hasNext()) {
                    String s = in.next();
                    if (s.equals("end")) {
                        break;
                    }
                    System.out.println("The value of i is: " + i++ + " and you entered " + s);
                }
                

                如果你真的想要一个 for 循环而不是 while,最好这样做:

                int i = 0;
                for (Scanner in = new Scanner(System.in); in.hasNext();) {
                    String s = in.next();
                    if (s.equals("end")) {
                        break;
                    }
                    System.out.println("The value of i is: " + i++ + " and you entered " + s);
                }
                

                根据测试子句中的字符串保留测试的最后一个变体如下所示:

                int i = 0;
                String s = "";
                for (Scanner in = new Scanner(System.in);
                     in.hasNext() && !s.equals("end"); 
                     s = in.next()) {
                    System.out.println("The value of i is: " + i++ + " and you entered " + s);
                }
                

                您还可以在s.equals("end") 之前添加一个空检查以确保安全。

                【讨论】:

                  【解决方案15】:

                  这不是很好的做法,因为有两件事:

                  1. for 循环旨在遍历数据集合
                  2. for 循环由迭代器初始状态、循环条件和迭代函数组成,相关

                  for 语句只是混合了两种不同的信息(流和计数器)。即使它确实有效,这样做也不是一个好习惯。

                  【讨论】:

                    【解决方案16】:

                    我本来只想发表评论,但我还没有代表。 我没有看到解释的是为什么你的 s 值没有改变。

                    在典型的 for 循环中:

                    for(a=1; a<=10; a+=1) {body}
                    

                    初始短语“a=1”仅作为初始化执行一次。

                    第三个短语“a+=1”在每个循环结束时执行一次,直到……

                    第二个短语“a>=10”的计算结果为假。

                    所以 for 循环将在“伪代码”中表示,如下所示:

                        a=1         // first phrase
                    :LoopLabel
                        {body}
                        a+=1        // third phrase
                        if (a<=10)  // second phrase (boolean evaluation)
                            then goto LoopLabel
                    

                    同样,您的示例在类似的伪代码中可能如下所示:

                        Scanner in = new Scanner(System.in);
                        int i = 0;
                        String s = in.next()
                    :LoopLabel
                        {
                            System.out.println("The value of i is: " + i + " and you entered " + s);
                        }
                        ++i
                        if (!s.equals("end"))
                            goto LoopLabel
                    

                    所以你的程序是一个无限循环的原因是's'的值只在进入你的循环时设置,并且在每次循环执行期间从未改变,这很可能是希望的。

                    【讨论】:

                      【解决方案17】:

                      这不是一个好习惯,可能是因为您将String s 与字符串进行比较,但您不是在比较值,而是在比较s 值的内存位置。

                      【讨论】:

                      • 没有内存位置比较,是普通的“等于”比较,会比较字符串的内容。
                      猜你喜欢
                      • 2015-06-21
                      • 2018-07-06
                      • 2016-07-27
                      • 1970-01-01
                      • 2015-01-22
                      • 2013-04-19
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-08-17
                      相关资源
                      最近更新 更多