如何在Java中从循环中删除while(true)?

3

我听说使用while(true)是一种不好的编程实践。

因此,我编写了以下代码来获取用户输入的一些数字(带有默认值)。 但是,如果用户输入-1,则会为他们退出程序。

那么,没有while(true)应该如何编写呢? 我可以考虑一个条件,使while循环立即结束,而不会继续进行下一次迭代吗?

以下是我目前的代码:

 public static void main(String[] args)
    {
        System.out.println("QuickSelect!");

        while (true)
        {
            System.out.println("Enter \"-1\" to quit.");

            int arraySize = 10;
            System.out.print("Enter the size of the array (10): ");
            String line = input.nextLine();
            if (line.matches("\\d+"))
            {
                arraySize = Integer.valueOf(line);
            }

            if (arraySize == -1) break;

            int k = 1;
            System.out.print("Enter the kth smallest element you desire (1): ");
            line = input.nextLine();
            if (line.matches("\\d+"))
            {
                k = Integer.valueOf(k);
            }

            if (k == -1) break;

            List<Integer> randomData = generateRandomData(arraySize, 1, 100);

            quickSelect(randomData, k);
        }
    }

11
在这种情况下,我认为while(true)没有问题。告诉你没有合法使用while(true)的人是错误的。 - Keith Randall
2
我同意Keith的观点 - 这听起来像是一个没有支持证据的笼统陈述。要知道规则,知道何时打破规则。 - duffymo
2
Java的编程风格是for(;;)而不是while(true) - 我似乎找不到参考资料,但所有Sun的代码和教程都使用for(;;)而不是while(true) - Pete Kirkham
2
@Pete:我认为大多数Java程序员认为while(true)更清晰,因此更好。 - Stephen C
@Pete 和 @Stephen:我认为整个 for(;;) 的写法是 C/C++ 时代的遗物,可能在一些(可能非常过时的)编译器上有更高的效率。这并不是保持这种习惯用法的好理由 :) - Edan Maor
我不喜欢使用 while(true)。理由: https://dev59.com/dnM_5IYBdhLWcg3wWRkF - Dave Jarvis
7个回答

11

while (true) 是可以的,保留它。

如果您有更自然的终止条件,我会建议使用它,但在这种情况下,正如其他答案所证明的那样,删除 while (true) 会使代码更难理解。


3

有一种单入单出(SESE)的思想认为,你不应该使用 breakcontinue 或者滥用异常来做相同的事情(某些情况下可能被视作滥用)。我认为这里的意思不是你应该使用辅助标志变量,而是要清晰地表达循环的后置条件。这使得对循环进行形式化推理变得容易。显然,这种理性的推理方式并不流行于未受过教育的大众(例如我自己)。

public static void main(String[] args) {
    ...
    do {
        ...
        if (arraySize == -1)  {
            ...
            if (k != -1) {
                ...
            }
        }
    } while (arraySze == -1 || k == -1);
    ...
}

实际代码会更加复杂,你自然会将输入、输出和核心“业务”逻辑分开,这样可以更容易地了解正在发生的事情。


2
    bool exit = false;
while (!exit) {
    ...
    ...
    if (k == -1) {
        exit = true;            
    }
    else {         
        List <Integer> ....;
        quickselect(.......);
    }
}

但是,正如之前所说的那样,在这种情况下使用while循环是有效的。其他选项只是在if语句的基础上构建,以检查布尔值并退出。


1

虽然像这样拥有一个循环在技术上并没有错,但有些人会认为它不如以下代码易读:

bool complete = false;

while (!complete)
{

    if (arraySize == -1)
    {
        complete = true;
        break;
    }
}

此外,有时候最好设置一个安全循环计数器来检查循环是否已经执行了1亿次或者比你预期的循环体更多的次数。这是一种安全的方式,可以确保错误不会导致程序“挂起”。相反,你可以给用户友好的提示:“抱歉,您发现了一个错误...程序现在将退出...”,在这里你将“完成”设置为true并结束程序或进行额外的错误处理。我曾经在生产代码中看到过这种情况,也许你也可以使用。

2
break语句会导致bool变量无用...我想你的意思是continue。 - Jorn
如果没有break,循环体的其余部分(为了简洁起见我省略了)仍将执行。因此,它是必需的。 - Charlie Salts
1
他的评论是,“bool 没用”。你不需要complete - Ned Batchelder
好的。如果需要break,那么flag就不需要了。这就引出了一个问题,while(!complete)的位置应该放什么?这个答案的逻辑正在打转。 - spender
我的观点很简单,就是“while true”不如“while not complete”易读。当然,提问者可以用自己的方式,只写“while true”,它也能正常工作。这是对一个争议性问题的争论性回答。 - Charlie Salts

0
问题在于你在循环中做了太多的事情,而没有将功能分离成简单的方法。
如果你想坚持过程化的方法,你可以将读取数组大小和 k 的操作移动到单独的方法中,并利用赋值的结果是被分配的值这一事实:
    for (int arraySize; ( arraySize = readArraySize ( input ) ) != -1;) {
        final int k = readKthSmallestElement ( input );

        List<Integer> randomData = generateRandomData(arraySize, 1, 100);

        quickSelect(randomData, k);
    }

然而,这仍然有点丑陋,封装性也不好。因此,不要将两个!= -1测试分别应用于不同的变量,而是将arraySizekrandomData封装在一个对象中,并创建一个方法从输入中读取数据,如果用户退出,则返回QuickSelect对象或null

    for ( QuickSelect select; ( select = readQuickSelect ( input ) ) != null; ) {
        select.generateRandomData();
        select.quickSelect();
    }        

你甚至可能想要进入下一个阶段,从输入中创建一系列QuickSelect对象,每个对象封装一个迭代的数据:
    for ( QuickSelect select : new QuickSelectReader ( input ) ) {
        select.generateRandomData();
        select.quickSelect();
    }        

QuickSelectReader实现了Iterable,迭代器具有创建QuickSelect对象的逻辑,该对象封装了arraySize、k、列表和快速选择操作。但这比过程式变体要多得多。

只有在想要在其他地方重用它时才会这样做;仅为使main()漂亮而付出的努力是不值得的。

还要注意,"-1"与正则表达式"\\d+"不匹配,因此您确实有一个无限循环。


0
如果你真的不喜欢 while(true),你总可以选择 for(;;)。我更喜欢后者,因为它似乎更简洁。

1
更少的“冗余”?这两种形式是等效的,因此同样冗余(或不冗余)。也许你的意思是“更少的字符”? - Stephen C
我不认同你的逻辑。很多事情可以是等价的,但这并不意味着信息冗余的平等。例如,双重否定等价于没有否定,但包含更多的冗余信息,对吧? - spender

0

在此,while ( true ) 完全没问题,因为条件实际上是“只要用户不想退出”!

另外,您可以在一行上提示输入两个值以简化逻辑,并使用 "q" 表示退出:这使得您可以将循环重构为 "while ( !line.equals("q") )"。


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