'做'而'与'同时'

问题描述 投票:74回答:27

可能重复: While vs. Do While When should I use do-while instead of while loops?

我已经编程了一段时间(2年的工作+ 4。5年的学位+ 1年的大学预科课程),而且我从未使用过do-while循环而不是被迫参加编程入门课程。如果我从未遇到过如此根本的事情,我会越来越感觉我编程错了。

可能是因为我没有遇到正确的情况吗?

有哪些例子需要使用do-while而不是一段时间?

(我的学校教育几乎都是用C / C ++编写的,我的工作是在C#中,所以如果有另一种语言绝对有意义,因为do-whiles的工作方式不同,那么这些问题并不适用。)

澄清...我知道whiledo-while之间的区别。在检查退出条件然后执行任务时。 do-while执行任务,然后检查退出条件。

c# c++ c while-loop do-while
27个回答
98
投票

如果你总是希望循环执行至少一次。这并不常见,但我会不时使用它。您可能想要使用它的一种情况是尝试访问可能需要重试的资源,例如

do
{
   try to access resource...
   put up message box with retry option

} while (user says retry);

4
投票

我看到这个问题得到了充分的回答,但是想添加这个非常具体的用例场景。您可能会更频繁地开始使用do ...

do
{
   ...
} while (0)

通常用于多行#defines。例如:

#define compute_values     \
   area = pi * r * r;      \
   volume = area * h

这适用于:

r = 4;
h = 3;
compute_values;

但是有一个问题:

if (shape == circle)  compute_values;

因为这扩展到:

if (shape == circle) area = pi *r * r;
volume = area * h;

如果将它包装在do ... while(0)循环中,它会正确扩展为单个块:

if (shape == circle)
  do
  {
    area = pi * r * r;
    volume = area * h;
  } while (0);

2
投票

到目前为止的答案总结了do-while的一般用法。但是OP问了一个例子,所以这里有一个:获取用户输入。但是用户的输入可能无效 - 因此您要求输入,验证它,如果它有效则继续,否则重复。

使用do-while,您可以在输入无效时获得输入。使用常规的while循环,您可以获得一次输入,但如果它无效,则会一次又一次地获取它,直到它有效。如果循环体变得更复杂,不难看出前者更短,更优雅,更易于维护。


2
投票

我已经将它用于多次读取相同结构的阅读器。

using(IDataReader reader = connection.ExecuteReader())
{
    do
    {
        while(reader.Read())
        {
            //Read record
        }
    } while(reader.NextResult());
}

1
投票

这是我的理论为什么大多数人(包括我)喜欢while(){}循环来做{} while():while(){}循环可以很容易地适应执行就像do..while()循环而相反不是真的。 while循环以某种方式“更通用”。程序员也喜欢容易掌握的模式。一个while循环在开始时说它的不变量是什么,这是一件好事。

这就是我对“更一般”的看法。拿这个做..循环:

do {
 A;
 if (condition) INV=false; 
 B;
} while(INV);

将此转换为while循环非常简单:

INV=true;
while(INV) {
 A;
 if (condition) INV=false; 
 B;
}

现在,我们采用while循环模型:

while(INV) {
     A;
     if (condition) INV=false; 
     B;
}

并将其转换为do..while循环,产生这种怪异:

if (INV) {
 do
 {
         A;
         if (condition) INV=false; 
         B;

 } while(INV)
}

现在我们在两端有两个检查,如果不变的更改,你必须在两个地方更新它。在某种程度上,就像工具箱中的专用螺丝刀一样,你从不使用,因为标准螺丝刀可以满足您的一切需求。


1
投票

我编程大约12年,仅3个月前我遇到了一个使用do-while非常方便的情况,因为在检查条件之前总是需要一次迭代。所以猜猜你的重要时刻在前面:)。


1
投票

我无法想象你如何在没有使用do...while循环的情况下走了这么长时间。

现在有一个在另一台显示器上,该程序中有多个这样的循环。它们都是以下形式:

do
{
    GetProspectiveResult();
}
while (!ProspectIsGood());

1
投票

这是我个人的观点,但这个问题需要根植于经验的答案:

  • 我已经用C语言编程了36年,而且我从不在常规代码中使用do / while循环。
  • 这个构造唯一引人注目的用途是在宏中,它可以通过do { multiple statements } while (0)将多个语句包装到一个语句中
  • 我已经看到了无数的do / while循环的例子,其中包含伪造错误检测或冗余函数调用。
  • 我对这种观察的解释是程序员在用do / while循环来思考时往往会错误地模拟问题。他们要么错过一个重要的结局条件,要么他们错过了他们移动到最后的初始条件的可能失败。

由于这些原因,我开始相信,哪里有一个do / while循环,有一个错误,我经常挑战新手程序员向我展示一个do / while循环,我无法发现附近的错误。

这种类型的循环可以很容易地避免:使用for (;;) { ... }并在适当的地方添加必要的终止测试。通常需要不止一次这样的测试。

这是一个经典的例子:

/* skip the line */
do {
    c = getc(fp);
} while (c != '\n');

如果文件不以换行符结束,则会失败。这种文件的一个简单例子是空文件。

这是一个更好的版本:

int c;  // another classic bug is to define c as char.
while ((c = getc(fp)) != EOF && c != '\n')
    continue;

或者,此版本还隐藏了c变量:

for (;;) {
    int c = getc(fp);
    if (c == EOF || c == '\n')
        break;
}

尝试在任何搜索引擎中搜索while (c != '\n');,你会发现这个错误(2017年6月24日检索):

ftp://ftp.dante.de/tex-archive/biblio/tib/src/streams.c,函数getword(stream,p,ignore),有一个do / while,肯定至少2个错误:

  • c被定义为char
  • 有一个潜在的无限循环while (c!='\n') c=getc(stream);

结论:避免使用qazxsw poi / qazxsw poi循环,并在看到错误时查找错误。


0
投票

do循环检查循环前的条件,while循环检查循环后的条件。这是有用的是你想要根据循环运行的副作用,或者像其他海报所说,如果你想让循环运行至少一次。

我知道你来自哪里,但while是最少使用的东西,而且我从未使用过自己。你做错了。

你做错了。这就像说某人做错了因为他们从未使用过do...while原语。它不是常用的。


0
投票

当我在文件开头读取一个标记值时,我使用了do-while,但除此之外,我认为这种结构不太常用是不正常的 - bytes确实是情境性的。

do while

0
投票

我遇到使用do-while / -- file -- 5 Joe Bob Jake Sarah Sue -- code -- int MAX; int count = 0; do { MAX = a.readLine(); k[count] = a.readLine(); count++; } while(count <= MAX) 循环的最常见情况是在一个基于某些输入运行的小程序控制台程序中,并且会重复用户喜欢的次数。显然,控制台程序没有时间运行是没有意义的;但超出第一次由用户决定 - 因此do / while而不仅仅是do

如果需要,这允许用户尝试一堆不同的输入。

while

我怀疑软件开发人员现在越来越少地使用while / do { int input = GetInt("Enter any integer"); // Do something with input. } while (GetBool("Go again?")); ,现在几乎每个阳光下的程序都有某种GUI。对于控制台应用程序更有意义,因为需要不断刷新输出以提供指令或向用户提示新信息。相比之下,使用GUI,向用户提供该信息的文本可以只是坐在表单上,​​而不需要以编程方式重复。


60
投票

如果编译器不能胜任优化,那么do-while会更好。 do-while只有一个条件跳转,而for和while则有条件跳转和无条件跳转。对于流水线且不进行分支预测的CPU,这可能会对紧密循环的性能产生很大影响。

此外,由于大多数编译器都足够智能来执行此优化,因此在反编译代码中找到的所有循环通常都是do-while(如果反编译器甚至不愿意从后向本地gotos重建循环)。


0
投票

我在读取文件时一直使用do-while循环。我使用了很多包含标题注释的文本文件:

do

我将使用do-while循环读取“column1 column2”行,以便我可以查找感兴趣的列。这是伪代码:

while

然后我会做一个while循环来读取文件的其余部分。


0
投票

作为一名geezer程序员,我的许多学校编程项目都使用文本菜单驱动的交互。几乎所有人都使用类似以下逻辑的主程序:

# some comments
# some more comments
column1 column2
  1.234   5.678
  9.012   3.456
    ...     ...

从上学日开始,我发现我更频繁地使用while循环。


0
投票

当我们查看结果集时,我在Oracle中看到的其中一个应用程序。

一旦你有了一个结果集,你首先从它获取(do)并从那一点开始..检查fetch是否返回一个元素(当找到元素时)..同样可能适用于任何其他“像fetch一样的“实现。


0
投票

我在一个函数中使用它,返回utf-8字符串中的下一个字符位置:

do {
    line = read_line();
} while ( line[0] == '#');
/* parse line */

请注意,此功能是从头脑中编写的,未经过测试。关键是你必须要做第一步,你必须先做,然后才能评估条件。


0
投票

任何类型的控制台输入都可以与do-while配合使用,因为您第一次提示,并在输入验证失败时重新提示。


0
投票

它是服务器/消费者中非常常见的结构:

do
    display options
    get choice
    perform action appropriate to choice
while choice is something other than exit

char *next_utf8_character(const char *txt) { if (!txt || *txt == '\0') return txt; do { txt++; } while (((signed char) *txt) < 0 && (((unsigned char) *txt) & 0xc0) == 0xc0) return (char *)txt; } 是一个DOWHILE (no shutdown requested) determine timeout wait for work(timeout) IF (there is work) REPEAT process UNTIL(wait for work(0 timeout) indicates no work) do what is supposed to be done at end of busy period. ENDIF ENDDO

有时等待工作(0)可以更便宜的CPU(甚至消除超时计算可能是一个改善,非常高的到达率)。此外,有许多排队理论结果使得在繁忙时期服务的数量成为重要的统计数据。 (参见例如Kleinrock - Vol 1.)

同理:

REPEAT UNTIL(cond)

其中do {...} while(!cond)放入主循环可能过于昂贵,或者可能是不支持高效DOWHILE (no shutdown requested) determine timeout wait for work(timeout) IF (there is work) set throttle REPEAT process UNTIL(--throttle<0 **OR** wait for work(0 timeout) indicates no work) ENDIF check for and do other (perhaps polled) work. ENDDO 类型操作的内核,或者可能是优先级排队可能使其他工作饿死并且油门被用作饥饿控制的情况。

这种类型的平衡看起来像是一个黑客,但它可能是必要的。盲目使用线程池将完全破坏使用具有私有队列的看管线程的性能优势,因为使用线程池而不是看管线程需要线程安全实现。

我真的不想进入关于伪代码的辩论(例如,是否应该在UNTIL中测试关闭请求)还是看管线程与线程池相关 - 这只是为了给出一个特定用例的风格。控制流程结构。


-1
投票

我在研究适当的循环时遇到了这种情况,以便用于我所拥有的情况。我相信这将完全满足一个常见的情况,其中do ... while循环是一个比while循环更好的实现(C#语言,因为你声明这是你的主要工作)。

我正在根据SQL查询的结果生成字符串列表。我的查询返回的对象是一个SQLDataReader。该对象有一个名为Read()的函数,它将对象前进到下一行数据,如果有另一行则返回true。如果没有其他行,它将返回false。

使用此信息,我想将每行返回到列表,然后在没有更多数据返回时停止。 A Do ... While循环在这种情况下效果最好,因为它确保在检查是否存在另一行之前将向列表添加项目。在检查while(条件)之前必须完成的原因是,当它检查时,它也会前进。在这种情况下使用while循环会导致它绕过第一行,因为该特定函数的性质。

简而言之:

这在我的情况下不起作用。

check for and do other work

这将。

waitany(waitcontrol*,n)

-1
投票
    //This will skip the first row because Read() returns true after advancing.
    while (_read.NextResult())
           {
               list.Add(_read.GetValue(0).ToString());
           }

   return list;

11
投票

我在TryDeleteDirectory函数中使用过它。就是这样的

do
{
    try
    {
        DisableReadOnly(directory);
        directory.Delete(true);
    }
    catch (Exception)
    {
        retryDeleteDirectoryCount++;
    }
} while (Directory.Exists(fullPath) && retryDeleteDirectoryCount < 4);

11
投票

当你想要至少执行一次某事时,同时有用。至于使用do while while while的一个很好的例子,假设您想要做以下事情:计算器。

您可以通过使用循环来检查这一点,并在每次计算后检查该人是否要退出程序。现在你可以假设一旦打开程序,这个人想要至少做一次这样做,你可以做以下事情:

do
{
    //do calculator logic here
    //prompt user for continue here
} while(cont==true);//cont is short for continue

10
投票

这是一种间接的答案,但这个问题让我思考它背后的逻辑,我认为这可能值得分享。

正如其他人所说,当你想要至少执行一次身体时,你会使用do ... while循环。但在什么情况下你想这样做?

好吧,我能想到的最明显的一类情况是,检查条件的初始(“未打底”)值与您想要退出时的值相同。这意味着您需要执行一次循环体以将条件置为非退出值,然后根据该条件执行实际重复。程序员如此懒惰,有人决定将其包含在控制结构中。

因此,例如,从超时的串行端口读取字符可能采用这种形式(在Python中):

response_buffer = []
char_read = port.read(1)

while char_read:
    response_buffer.append(char_read)
    char_read = port.read(1)

# When there's nothing to read after 1s, there is no more data

response = ''.join(response_buffer)

请注意重复的代码:char_read = port.read(1)。如果Python有一个do ... while循环,我可能已经使用过:

do:
    char_read = port.read(1)
    response_buffer.append(char_read)
while char_read

为创建循环新范围的语言带来的额外好处:char_read不会污染函数名称空间。但请注意,有一种更好的方法可以做到这一点,那就是使用Python的None值:

response_buffer = []
char_read = None

while char_read != '':
    char_read = port.read(1)
    response_buffer.append(char_read)

response = ''.join(response_buffer)

所以这就是我的观点的关键:在具有可空类型的语言中,initial_value == exit_value的情况出现的频率要低得多,这可能就是你没有遇到它的原因。我并不是说它永远不会发生,因为有时函数会返回None来表示有效条件。但是,在我匆忙和简要考虑的意见中,如果您使用的语言不允许表示的值,则会发生更多这种情况:此变量尚未初始化。

这不是完美的推理:实际上,现在空值很常见,它们只是形成变量可以采用的有效值集合中的另一个元素。但实际上,程序员有办法区分处于合理状态的变量,其中可能包括循环退出状态,并且处于未初始化状态。


7
投票

我在学校的时候用了很多东西,但从那以后就没用了。

理论上,当您希望循环体在退出条件检查之前执行一次时,它们非常有用。问题是,对于我不想先检查的少数情况,通常我希望在循环体的中间而不是在最末端进行退出检查。在这种情况下,我更喜欢使用着名的for (;;)和身体某处的if (condition) exit;

事实上,如果我在循环退出条件上有点不稳定,有时我发现在需要的情况下开始将循环编写为带有退出语句的for (;;) {}是有用的,然后当我完成时我可以看到它是否可以通过在for的括号内移动初始化,退出条件和/或增加代码来“清理”。


6
投票

在这种情况下,您总是需要运行一段代码,并且根据其结果,可能需要更多次。使用常规while循环也可以产生相同的效果。

rc = get_something();
while (rc == wrong_stuff)
{
    rc = get_something();
}

do
{
    rc = get_something();
}
while (rc == wrong_stuff);

6
投票

do while是你想要至少运行一次代码块。另一方面,while并不总是根据指定的标准运行。


5
投票

就这么简单:

前提条件与后置条件

  • while(cond){...} - 前提条件,它仅在检查后执行代码。
  • do {...} while(cond) - postcondition,代码至少执行一次。

既然你知道秘密......明智地使用它们:)

© www.soinside.com 2019 - 2024. All rights reserved.