'робити ... поки' проти 'поки'


86

Можливі дублікати:
While порівняно Do Do
When Коли слід використовувати do-while замість циклів while?

Я займаюсь програмуванням вже деякий час (2 роки роботи + 4,5 роки ступінь + 1 рік дошкільного коледжу), і я ніколи не використовував цикл виконуваних робіт, окрім того, щоб бути змушеним на курсі «Вступ до програмування». У мене зростає відчуття, що я неправильно програмую, якщо ніколи не стикаюся з чимось таким фундаментальним.

Можливо, я просто не зіткнувся з правильними обставинами?

Наведіть кілька прикладів, коли замість часу потрібно було б виконувати функцію виконування часу?

(У моєму навчанні майже все було на C / C ++, а моя робота - на C #, тому, якщо є інша мова, де це абсолютно має сенс, оскільки до-білі працюють по-різному, то ці питання насправді не стосуються.)

Щоб уточнити ... Я знаю різницю між a whileта a do-while. Поки перевіряє стан виходу, а потім виконує завдання. do-whileвиконує завдання, а потім перевіряє стан виходу.


42
Для того, що це варто, після десятиліття програмування, як моєї кар'єри, я використав цикл виконання, як раз чи два.
Matt Greer

@Matt - Від цього мені стає краще. Принаймні це означає, що я не одна.
mphair

2
Я підтримаю Метта і додам до цього ще близько десятиліття. Я також рідко коли-небудь використовую цикл do-while.
квентін-старін

БАГАТО дублікатів. Ось один , що посилання на купу з них: stackoverflow.com/questions/3094972 / ...
gnovice

3
Правильним “головним запитанням”, схоже, є stackoverflow.com/questions/224059/…
Donal Fellows

Відповіді:


115

Якщо ви завжди хочете, щоб цикл виконувався хоча б раз. Це не часто, але я часом користуюся ним. Один із випадків, коли ви можете використовувати його, - це спроба отримати доступ до ресурсу, який може вимагати повторної спроби, наприклад

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

} while (user says retry);

3
Отже, я думаю, це могло б відбуватися під "Я не стикався з цією обставиною".
mphair

7
чесно кажучи, я дуже рідко стикаюся з обставиною, коли мені просто потрібно зробити до..тоді ..
Стен Р.

1
Можливо, трохи короткий, але Боб має рацію. Do / while слід використовувати, коли ви хочете запустити блок коду хоча б один раз . Вам слід використовувати цикл while, коли ви не знаєте, чи хочете ви навіть запускати його один раз. Тим не менш, я можу сказати вам, що, маючи приблизно 15 років досвіду розробки, я використовував у рази більше циклів while, ніж цикли while / while.
ConsultUtah

1
Я також використовував їх час від часу, хоча і рідко. Через деякий час вони, як правило, також переробляються в звичайний цикл while - просто тому, що області навколо нього змінювались і робили щось ... при цьому більш незграбне, ніж було спочатку.
Джошуа

2
Це досить часто. Але я не розумію, чому у вашому прикладі ви не використали б цикл while. І я не розумію, чому цю відповідь голосують.

65

do-while краще, якщо компілятор не компетентний в оптимізації. do-while має лише один умовний стрибок, на відміну від for і while, який має умовний стрибок і безумовний стрибок. Для процесорів, які конвеєруються і не передбачають прогнозування розгалужень, це може суттєво змінити продуктивність жорсткого циклу.

Крім того, оскільки більшість компіляторів достатньо розумні, щоб виконати цю оптимізацію, усі цикли, знайдені в декомпільованому коді, зазвичай виконуються (якщо декомпілятор взагалі заважає реконструювати цикли із відсталих локальних гото).


5
+1 для додавання відповідної технічної інформації до вибору між цикловими формами.
квентін-старін

2
Не тільки передчасні, але й марні. Якщо ви дійсно хотіли цикл while, тобто можливість ітерації 0 разів, вам потрібно вкласти цикл у if, який повертає цей стрибок назад.
JeremyP

3
@Jeremy: Перехід, про який ви говорите, знаходиться за межами циклу, він не виконується N разів.
Ben Voigt

1
Розглянемо також класичне використання режиму виконуваної роботи в пристрої Даффа (де цикл while перетворюється на нерозгорнутий режим виконуваної роботи з більшим кроком та таблицею переходу для обробки решти), що різко зменшує накладні витрати на цикл - у таких додатках, як memset, memcmp, memcpy це може збільшити пропускну здатність у 10 разів.
Бен Войгт,

@BenVoigt Я знаю, що вашому коментарю вже десять років тому, але пристрій Даффа не слід згадувати без дуже важливого застереження - він став менш ефективним для сучасних компіляторів, оскільки сучасні компілятори зазвичай можуть розумно розгортати прямі цикли, оптимальні для цільової машини , хоча вони, як правило, не мають логіки для з’ясування наміру пристрою Даффа, і, отже, не можуть оптимізувати його майже так само добре. Що є чудовим уроком щодо оптимізації коду для якоїсь цільової машини, а не для абстрактного "достатньо вдосконаленого оптимізатора".
mtraceur

12

Я використав це у функції TryDeleteDirectory. Це було приблизно так

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

Приємно. Це перший приклад, який насправді має сенс загорнутись у do ... while.
Джошуа

4
Чому / як це краще за стандарт while?
Джош Стодола,

Цикл завжди буде виконуватися один раз, тому, можливо, ви захочете щось зробити, а потім спробуйте ще раз, якщо це не вдасться. Якщо ви використовуєте звичайний цикл while, ви перевірите, чи він не вдається, ще до того, як ви зробили це на першій ітерації.
NibblyPig

1
@SLC Але чи не хочете ви перевірити, чи існує каталог у будь-якому випадку раніше?
Джош Стодола,

1
@Josh У цьому прикладі так. Але якби ви намагалися відкрити з'єднання із зайнятим сервером, спочатку підключіться і подивіться, чи відповідь "зайнята". Якби це було, ви спробували б ще кілька разів, перш ніж здаватися.
NibblyPig

11

Виконання while корисно, коли ви хочете виконати щось хоча б раз. Що стосується гарного прикладу використання do while проти while, скажімо, ви хочете зробити наступне: Калькулятор.

Ви можете підійти до цього, використовуючи цикл і перевіряючи після кожного обчислення, чи хоче людина вийти з програми. Тепер ви можете, напевно, припустити, що після відкриття програми людина хоче зробити це хоча б один раз, щоб ви могли зробити наступне:

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

2
Власне, тепер, коли ви про це згадуєте, будь-який інтерфейс, керований меню на основі консолі, також добре би працював у циклі do ... while. Наприклад, запит щодо опцій (по одній клавіші за раз) і вихід, лише коли вони вирішили продовжити або вийти.
Джошуа

7
continue- ключове слово; D
Thomas Eding

Атрінітіс: Лол ... Я прослизнув. Дякую, що ви мене виправили.

== trueне потрібен. cont == trueякщо cont істина, повертає істина. Це абсолютно марно.
Mr.DeleteMyMessages

11

Це свого роду непряма відповідь, але це питання змусило мене задуматись над логікою, яка лежить в основі цього, і я подумав, що цим, можливо, варто поділитися.

Як сказали всі інші, ви використовуєте 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не забруднює простір імен функції. Але зверніть увагу, що є кращий спосіб зробити це, і це за допомогою Noneзначення Python :

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щоб означати дійсну умову. Але, на мій поспішний і коротко обміркований погляд, це сталося б набагато більше, якби в мовах, які ви використовували, не було значення, яке означає: ця змінна ще не ініціалізована.

Це не ідеальне міркування: насправді, тепер, коли нульові значення є загальними, вони просто утворюють ще один елемент набору допустимих значень, які може приймати змінна. Але практично, програмісти мають спосіб розрізнити змінну, яка перебуває в розумному стані, яке може включати стан виходу з циклу, і перебуває в неініціалізованому стані.


Це одна з найцікавіших відповідей, яку я прочитав тут. Гарний внесок.
Боб Мур,

Дві версії не є еквівалентними. Якщо перше прочитане повертається None, whileверсія правильно нічого не додає до буфера відповідей, але ваша doверсія завжди щось додає.
Девід Стоун

@DavidStone read()ніколи не повинен повертатися None. Якщо ви використовуєте бібліотеку там, де вона використовується, передумова не застосовується (тобто те, що вартове значення не входить до набору можливих контрольних значень).
помер

З вашим останнім редагуванням, я бачу, що я повинен був сказати ''замість None. Якесь значення, яке оцінюється як хибне. Запишіть це до мого незнайомства з readфункцією Python .
Девід Стоун

@DavidStone Я не розумію. Якщо read()повернеться '', до рядка нічого не буде додано, він порожній.
detly

7

Коли я навчався в школі, я використовував їх, але з того часу не так вже й багато.

Теоретично вони корисні, коли потрібно, щоб тіло циклу виконувалось один раз перед перевіркою стану виходу. Проблема полягає в тому, що для тих декількох випадків, коли я не хочу перевірки спочатку, зазвичай я хочу перевірку виходу в середині тіла циклу, а не в самому кінці. У цьому випадку, я вважаю за краще використовувати добре відомий for (;;)з if (condition) exit;де - то в тілі.

Насправді, якщо я трохи хиткий щодо умови виходу з циклу, іноді я вважаю корисним почати писати цикл як а for (;;) {}з оператором виходу, де це потрібно, і тоді, коли я закінчу, я зможу зрозуміти, чи може це бути " очищено ", перемістивши ініціалізації, умови виходу та / або код збільшення в forдужки.


5
З цікавості ... чому "за (;;)" замість "поки (правда)"?
mphair

4
Можливо, просто смак. Я зрівнявся for(;;)з "нескінченною петлею", тоді як while(true)я повинен зупинитися і подумати. Можливо, це тому, що я кодував C до того, як був true. Ще в той день у нас був TRUEлише макрос, і якщо щось глючило, мені довелося би переконати себе, що макрос не був 0якось перевизначений (це сталося!). З for(;;)немає ніяких шансів , що. Якщо ви наполягаєте на формі while, я б майже волів побачити while (1). Звичайно, тоді деякі можуть задатися питанням, чи це не буква l ...
TED,

1
@mphair: Так, якщо ви берете до уваги текст, який редактор вставляє, коли курсив або codeifyтекст, ви можете просто ввести це безпосередньо в поле коментаря самостійно. Я бачив, як деякі люди вкладають гіперпосилання, але це для мене занадто жорстко.
TED,

2
@TED: Моїм улюбленим позначенням циклу-до-явного виходу є "do {} while (1);". До речі, у роботі вбудованих систем моєю нормальною парадигмою циклу є "i = 10; do {} while (- i);". Для жорстких петель це набагато швидше, ніж типовий для (); Я вважаю, що це читабельніше використовувати це послідовно як шаблон циклу, коли не потрібен підрахунок, ніж довільно використовувати for () у випадках, не критичних до продуктивності.
supercat

2
Додаючи до розподілу while (1) та (;;), я бачив код, який використовує for (EVER) з EVER, визначеним як ;;
asr

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) - післяумова, код виконується принаймні один раз.

Тепер, коли ви знаєте секрет .. використовуйте їх з розумом :)


1
Як я вже сказав, я знаю, як вони працюють, я просто не був впевнений, в якому випадку один має сенс над іншим.
mphair

Логічно, що ви використовуєте do {}, тоді як коли точно знаєте, що вам потрібно пройти принаймні один раз через цикл. Як, наприклад, якщо у вас є функція, яка знаходить елемент у масиві, і ви вже розмістили масив if.IsEmpty (), а потім повертаєте; . Якщо він пройшов цю перевірку, ви можете сміливо використовувати do {}. do-while також легко сприймати, як повторюваний цикл: "Я їм, поки не насичусь" перекладається як зробити {eat ();} while (! задоволений ();). Тестування стану перед циклом вважається більш безпечним .. і саме тому воно використовується частіше.
Ioan Paul Pirau

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 ви отримуєте введення, коли введення недійсне. За допомогою звичайного циклу while ви отримуєте введення один раз, але якщо воно недійсне, ви отримуєте його знову і знову, поки воно не буде дійсним. Неважко помітити, що перший коротший, елегантніший і простіший в обслуговуванні, якщо тіло петлі ускладнюється.


Я думаю, що причина, по якій я їх ніколи не використовував для цього, полягає в тому, що я зазвичай в кінцевому підсумку повідомляю користувачеві, що не було з їх введенням. Що означає, що умовне значення буде в центрі циклу, і я б просто breakввів правильний ввід.
mphair

@mphair: Насправді, для такої ситуації я часом можу бути більш схильним використовувати "do {} while (0);" цикл, використовуючи "продовжити;" якщо введення користувачем неприйнятного. Якщо проблема і повідомлення лише одна, використовуйте "do {} while (1);" і "перерва;" працює добре, але якщо є кілька причин вимагати повторного входу, "продовжити"; конструкція працює більш приємно.
supercat

2

Я використовував його для зчитувача, який читає одну і ту ж структуру кілька разів.

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

1

Я використовував а, do whileколи читаю сторожове значення на початку файлу, але крім цього, я не вважаю ненормальним те, що ця структура не надто часто використовується 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)

1

Ось моя теорія, чому більшість людей (включаючи мене) вважають за краще цикли while () {} робити {} while (): Цикл while () {} можна легко адаптувати для роботи як цикл do.. While (), а навпаки не відповідає дійсності. Цикл while є певним чином "більш загальним". Крім того, програмісти люблять легко зрозумілі шаблони. Цикл while з самого початку говорить, що таке його інваріант, і це приємно.

Ось, що я маю на увазі щодо "більш загальної" речі. Візьміть цей цикл do.. 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
Заміна "do {} while (x);" цикл у "while (x);" цикл вимагає, щоб X або була умовою, яку можна легко "грунтувати", або щоб вона була "природно" істинною при першому проходженні. Для мене оператори праймінгу перед циклом означають, що щось у циклі потребує грунтування, а while (x) {}; "цикл означає, що цикл може виконуватися нуль разів. Якщо я хочу цикл, який буде виконуватися принаймні один раз, я використовуйте цикл "do {} while (x);".
supercat

1

Я програмую близько 12 років, і лише 3 місяці тому я зіткнувся з ситуацією, коли було дійсно зручно використовувати do-while, оскільки одна ітерація завжди була необхідною перед перевіркою стану. Тож здогадайтесь, ваш великий час попереду :).


1
Майже щоразу, коли я бачив щось робити ... хоча це було через недостатнє розуміння проблеми і спричинило якийсь жахливий хак. Є винятки, але загалом кажучи, якщо у вас є щось робити ... поки, вам слід переосмислити те, що ви робите ... :-)
Брайан Ноблауч,

2
Якщо ви десь там, тоді виконайте-буде виключено з усіх мов програмування, щоб не заплутати програмістів :).
Нарек,

@Narek: Минуло ще близько семи років з того часу, як ви написали це, чи було у вас більше обставин, коли do whileбуло б кращим рішенням без жалю?
chqrlie

Так, мабуть, ще раз: D
Нарек

1

Я не уявляю, як ти пройшов так довго, не використовуючи do...while циклу.

Зараз є один на іншому моніторі, і в цій програмі є кілька таких циклів. Вони всі у формі:

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

1

Це досить поширена структура на сервері / споживачі:

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

REPEAT UNTIL(cond)будучиdo {...} while(!cond)

Іноді очікування на роботу (0) може бути дешевшим для процесора (навіть усунення розрахунку тайм-ауту може бути покращенням при дуже високих показниках надходження). Більше того, є багато результатів теорії черг, які роблять кількість обслуговування в напружений період важливою статистикою. (Див., Наприклад, Кляйнрок - Том 1.)

Аналогічно:

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

де check for and do other workможе бути надзвичайно дорого вкласти в основний цикл або, можливо, ядро, яке не підтримує ефективну waitany(waitcontrol*,n)операцію типу, або, можливо, ситуація, коли пріоритетна черга може заморити іншу роботу, а дросель використовується як контроль голоду.

Цей тип балансування може здатися зломкою, але може знадобитися. Сліпе використання пулів потоків цілком перемогло б переваги продуктивності використання потоку наглядача з приватною чергою для складної структури даних із високою швидкістю оновлення, оскільки використання пулу потоків, а не потоку службовця, вимагало б безпечної реалізації потоків.

Я справді не хочу вступати в дебати щодо псевдокоду (наприклад, чи потрібно тестувати запит на вимкнення до UNTIL) чи потоків доглядача проти пулів потоків - це просто має на меті надати смак конкретному випадку використання структура потоку управління.


1

Це моя особиста думка, але це запитання вимагає відповіді, що ґрунтується на досвіді:

  • Я програмую на C 38 років, і я ніколи не використовую do/ whileцикли в звичайному коді.

  • Єдине переконливе використання цієї конструкції - це макроси, де вона може обернути декілька операторів в один оператор через a do { multiple statements } while (0)

  • Я бачив незліченну кількість прикладів do/ whileциклів із помилковим виявленням помилок або надмірними викликами функцій.

  • Моє пояснення цього спостереження полягає в тому, що програмісти, як правило, неправильно моделюють проблеми, коли думають за допомогою do/ whileциклів. Вони або пропускають важливу кінцеву умову, або пропускають можливий збій початкової умови, яку вони переносять до кінця.

З цих причин я прийшов до думки, що там, де є do/ whileloop, є помилка , і я регулярно кидаю виклик програмістам-початківцям показати мені do/ whileloop, де я не можу помітити помилку поблизу.

Цього типу циклу можна легко уникнути: використовуйте a 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');в будь-якій пошуковій системі, і ви знайдете такі помилки, як ця (отримано 24 червня 2017 року):

У 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);

Висновок: уникайте циклів do/ whileциклів та шукайте помилок, коли їх бачите.


1
Я вважаю за краще уникати while() {}циклів, тому що вони безпечніші з точки зору первинних перевірок, тому, як правило, ви змушуєте лінуватися думати про кращий алгоритм, ставите безліч непотрібних перевірок помилок тощо. "Відсутність do{}while()використання" - це ознака поганого (безмозгового) або програміст-новачок, виробляють низьку якість і повільніший код. Тож я спочатку запитую себе: "Це суворо випадок while () {}?" Якщо ні - я просто спробую написати цикл do while, незважаючи на те, що це може вимагати більше обдумування та точності вхідних даних
xakepp35

Я розумію вашу відповідь, але я міг би подумати про якийсь можливий сценарій, коли це насправді було б краще. Будь ласка, подивіться на while: prnt.sc/v2zy04 або на do while: prnt.sc/v2zyka . Я не був програмістом так давно, як і ви, але я не міг там знайти помилку. Цілком зрозуміло, що do while є чистішим, оскільки для входу в цикл не потрібна додаткова перевірка часу. Якщо ми говоримо про оптимізацію коду, то я б сказав, що це досить чисто.
Рой Берріс,

@RoyBerris: ваш приклад цікавий. У програмі на С функції, що використовуються всередині циклу, потребуватимуть додаткових тестів, щоб переконатися, що вони не провалились. Це додало б додаткового безладу і / breakабо returnтверджень. Перетворення циклу do/ whileв for(;;)цикл буде більш послідовним IMHO. Мій остракізм щодо цього твердження схожий на мою відмову використовувати strncpy() навіть у тих рідкісних випадках, коли це був би правильний інструмент: не дозволяйте випадковим читачам уявляти, що ці конструкції нешкідливі, оскільки в більшості випадків вони є джерелами важких для пошуку помилок .
chqrlie

@chqrlie, побачивши оригінальну відповідь, стосувалася будь-якої мови C, я гадаю, обидві відповіді підійдуть. C # набагато "заощаджує", ніж їх попередники, тому технічно це було б швидше, якщо робити do while.
Рой Берріс,

0

whileцикли перевіряють стан перед циклом, do...whileцикли перевіряють стан після циклу. Це корисно, якщо ви хочете базувати умову на основі побічних ефектів від запущеного циклу або, як сказано в інших плакатах, якщо ви хочете, щоб цикл запускався хоча б один раз.

Я розумію, звідки ти родом, але do-whileце те, що більшість використовують рідко, і я ніколи не використовував себе. Ви не робите це неправильно.

Ви не робите це неправильно. Це все одно, що сказати, що хтось робить це неправильно, бо ніколи не використовував byteпримітив. Це просто не так часто використовується.


0

Найпоширеніший сценарій, з яким я стикаюся, коли я використовую цикл do/, while- це невелика консольна програма, яка працює на основі певного вводу і повторюватиме стільки разів, скільки сподобається користувачеві. Очевидно, що немає сенсу для консольної програми запускатися жодного разу; але після першого разу це залежить від користувача - отже do/ whileа не просто while.

Це дозволяє користувачеві випробувати купу різних входів за бажанням.

do
{
   int input = GetInt("Enter any integer");
   // Do something with input.
}
while (GetBool("Go again?"));

Я підозрюю, що розробники програмного забезпечення використовують do/ whileвсе менше в наші дні, тепер, коли практично кожна програма під сонцем має якийсь графічний інтерфейс. Це має більший сенс для консольних додатків, оскільки потрібно постійно оновлювати вихідні дані, щоб надавати інструкції або спонукати користувача до нової інформації. На відміну від графічного інтерфейсу, текст, що надає інформацію користувачеві, може просто розміщуватися у формі і ніколи не потребує програмного повторення.


0

Я постійно використовую цикли do-while під час читання у файлах. Я працюю з великою кількістю текстових файлів, які містять коментарі в заголовку:

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

я буду використовувати цикл виконуваної роботи, щоб прочитати рядок "column1 column2", щоб я міг шукати стовпець, що цікавить. Ось псевдокод:

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

Потім я виконаю цикл while, щоб прочитати решту файлу.


Підтримка коментарів у будь-якому місці файлу буде тривіальною (і спрощення коду загалом), якщо ви просто ставите if (line[0]=='#') continue;відразу після read_lineдзвінка у ваш основний цикл while ...
R .. GitHub STOP HELPING ICE

Не можу зрозуміти, як би я реалізував вашу пропозицію знайти заголовок. (Данг, не можу зрозуміти, як форматувати код у коментарях, допоможіть, будь ласка!) header = false; while (! заголовок) {line = read_line (); якщо (рядок [0] == '#') продовжити; заголовок = істина; } це вам зрозуміліше / простіше?
літає

0

Будучи програмістом-гізером, багато моїх шкільних проектів програмування використовували взаємодію, керовану текстовим меню. Практично всі використовували щось на зразок наступної логіки для основної процедури:

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

Зі шкільних днів я виявив, що частіше використовую цикл while.


0

Одне з додатків, яке я бачив, - це в Oracle, коли ми розглядаємо набори результатів.

Після того, як у вас є набір результатів, ви спочатку отримуєте з нього (do) і з цього моменту .. перевіряйте, чи повертає елемент вилучення чи ні (поки елемент знайдений ..) .. Те саме може бути застосовано до будь-якого іншого " подібні до вибірки реалізації.


0

Я використовував його у функції, яка повертає наступну позицію символу в рядку utf-8:

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;
}

Зверніть увагу, що ця функція написана з розуму і не перевірена. Сенс у тому, що ти все одно повинен зробити перший крок, і це ти повинен зробити перед тим, як оцінити стан.


Це неприємний спосіб писати *(unsigned char *)txt-0x80U<0x40.
R .. GitHub СТОП ДОПОМОГАЙ ЛЕД

0

Будь-який тип вводу консолі добре працює з do-while, тому що ви вказуєте перший раз і повторно подаєте запит, коли перевірка вводу не вдається.


0

Незважаючи на те, що тут є багато відповідей, я вважаю це. Все зводиться до оптимізації. Я покажу два приклади, коли один швидший, ніж інший.

Випадок 1: while

string fileName = string.Empty, fullPath = string.Empty;

while (string.IsNullOrEmpty(fileName) || File.Exists(fullPath))
{
    fileName = Guid.NewGuid().ToString() + fileExtension;
    fullPath = Path.Combine(uploadDirectory, fileName);
}

Випадок 2: do while

string fileName = string.Empty, fullPath = string.Empty;

do
{
    fileName = Guid.NewGuid().ToString() + fileExtension;
    fullPath = Path.Combine(uploadDirectory, fileName);
}
while (File.Exists(fullPath));

Отже, двоє будуть робити абсолютно однакові речі. Але є одна принципова відмінність, яка полягає в тому, що для введення в той час як потрібен додатковий вислів. Що потворно, бо скажімо, усі можливі сценарії Guidкласу вже прийняті, за винятком одного варіанту. Це означає, що мені доведеться крутитися 5,316,911,983,139,663,491,615,228,241,121,400,000разів. Кожного разу, коли я доходжу до кінця моєї заяви, мені потрібно буде зробитиstring.IsNullOrEmpty(fileName) перевірку. Отже, це зайняло б трохи, незначну частку роботи процесора. Але виконайте це дуже маленьке завдання, раз можливе поєднанняGuid класу, і ми говоримо про години, дні, місяці чи додатковий час?

Звичайно, це надзвичайний приклад, тому що ви, мабуть, цього не побачите у виробництві. Але якщо ми подумаємо про алгоритм YouTube, цілком можливо, що вони зіткнуться з генерацією ідентифікатора, де деякі ідентифікатори вже були взяті. Тож це зводиться до великих проектів та оптимізації.


0

Мені подобається розуміти ці два слова:
while-> 'повторювати до',
do ... while-> 'повторити, якщо'.


-1

Я натрапив на це, досліджуючи правильний цикл для використання в моїй ситуації. Я вважаю, що це повністю задовольнить типову ситуацію, коли цикл do .. while є кращою реалізацією, ніж цикл while (мова C #, оскільки ви заявили, що це ваша основна робота).

Я створюю список рядків на основі результатів запиту SQL. Повернутий об’єкт за моїм запитом - це SQLDataReader. Цей об'єкт має функцію Read (), яка переміщує об'єкт до наступного рядка даних і повертає true, якщо був інший рядок. Поверне значення false, якщо немає іншого рядка.

Використовуючи цю інформацію, я хочу повернути кожен рядок до списку, а потім зупинитись, коли більше немає даних для повернення. Цикл "Зроби ...", хоча найкраще працює в цій ситуації, оскільки гарантує, що додавання елемента до списку відбуватиметься ДО того, як перевірити, чи є інший рядок. Причиною цього має бути ПЕРЕД перевіркою while (умова) полягає в тому, що коли він перевіряє, він також просувається вперед. Використання циклу while у цій ситуації призведе до обходу першого рядка через характер цієї конкретної функції.

Коротко:

Це не спрацює в моїй ситуації.

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

   return list;

Це буде.

    //This will make sure the currently read row is added before advancing.
    do
        {
            list.Add(_read.GetValue(0).ToString());
        } 
        while (_read.NextResult());

    return list;

Використання SQLDataReader буде вкладеними циклами. Внутрішній виклик Read()повинен бути циклом while, оскільки перший Read()виклик звертається до першого рядка. Зовнішній дзвінок NextResult()повинен бути виконуваним, оскільки перший набір результатів активний до першого NextResult()дзвінка. Фрагменти коду не мають особливого сенсу, особливо тому, що коментарі і не відповідають коду, і помилкові.
Бен

-1
Console.WriteLine("hoeveel keer moet je de zin schrijven?");
        int aantal = Convert.ToInt32(Console.ReadLine());
        int counter = 0;

        while ( counter <= aantal)
        {
            Console.WriteLine("Ik mag geen stiften gooien");
            counter = counter + 1;

Ви повинні відповісти на власне запитання так, ніби збираєтеся відповісти на чуже. Включав пояснення, а також код
Саймон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.