3

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

因此,我编写了以下代码来从用户那里获取一些数字(使用默认值)。但是,如果用户碰巧输入了-1,那么它将为他们退出程序。

那么这应该怎么写呢(真的)?我可以想出一个条件来使 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);
        }
    }
4

7 回答 7

11

while (true)很好。收下。

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

于 2009-10-03T22:19:32.537 回答
3

有一个单入口单出口 (SESE) 学派建议您不应该使用breakcontinue滥用异常来为某些滥用价值做同样的事情)。我相信这里的想法不是你应该使用一些辅助标志变量,而是清楚地说明循环的后置条件。这使得以前对循环进行推理变得容易处理。明明是用有理有据的推理形式,所以不受洗不干净的群众(比如我自己)的欢迎。

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

真正的代码会更复杂,你自然会(!)分离出输入、输出和核心“业务”逻辑,这样可以更容易地看到发生了什么。

于 2009-10-04T00:40:31.280 回答
2
    bool exit = false;
while (!exit) {
    ...
    ...
    if (k == -1) {
        exit = true;            
    }
    else {         
        List <Integer> ....;
        quickselect(.......);
    }
}

但正如之前所说,您的 while 循环在这种情况下是有效的用法。其他选项将简单地基于 if 语句来检查布尔值并退出。

于 2009-10-03T22:09:19.257 回答
1

虽然像这样的循环在技术上并没有错,但有些人会争辩说它不像以下那样可读:

bool complete = false;

while (!complete)
{

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

此外,有时最好有一个安全循环计数器来检查以确保循环没有经过,例如,1 亿次迭代,或者比您对循环体预期的大得多的数字。这是确保错误不会导致程序“挂起”的安全方法。相反,您可以给用户一个友好的“我们很抱歉,但您发现了一个错误.. 程序现在将退出..”,您将“完成”设置为 true,然后结束程序或进行额外的错误处理。我已经在生产代码中看到了这一点,并且可能会或可能不会是您会使用的东西。

于 2009-10-03T22:11:05.367 回答
0

如果你真的不喜欢while(true)你总是可以去for(;;)。我更喜欢后者,因为它看起来不那么多余。

于 2009-10-03T22:17:53.523 回答
0

while ( true ) 在这里非常好,因为条件真的是“当用户不想退出时”!

或者,您可以在一行中提示输入两个输入以简化逻辑,并使用“q”表示退出:这允许您将循环重构为“while (!line.equals("q"))”。

于 2009-10-03T22:27:48.900 回答
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封装和在一个对象中,并创建一个从输入读取数据的方法,并返回一个对象或用户退出:arraySizekrandomDataQuickSelectnull

    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"它与 regex 不匹配"\\d+",因此您确实有一个无限循环。

于 2009-10-03T22:39:26.380 回答