1

Тема: Оформлення кількох випадків циклів

Я помітив, що не можу придумати загальної норми для оформлення кількох ситуацій з циклами. Можливо, справа тут в тому, що я дарма ці ситуації об'єдную, і там немає і не може бути загальних норм? Чи просто мені не траплявся нормальний код? Чи я пропустив ці випадки саме тому, що код читався легко, і мені не спало на думку, що це мої "складні" випадки?
Отже, випадки:
1. Кілки і жердини: в паркані n горизонтальних жердин і, відповідно, n+1 кілків. Треба в циклі їх пофарбувати. Як робити краще - фарбувати "зайвий" кілок перед циклом чи після? Що легше читається? Чи це залежить від конкретної ситуації?

приклад коду
Paint( firstPin );
for( pin = firstPin.next(), bar = firstBar; bar && pin; pin = pin.next(), bar = bar.next() )
{
  Paint( bar );
  Paint( pin );
}

чи

for( pin = firstPin, bar = firstBar; bar && pin; pin = pin.next(), bar = bar.next() )
{
  Paint( pin );
  Paint( bar );
}
Paint( pin );

2. Окремий випадок, коли циклу не відбувається: якщо умова циклу від самого початку не виконується, слід обробити окрему ситуацію. При цьому виникає комбінація

if( X ) 
{
  while( X ) 
  {}
}
else
  {}

з двома повтореннями умови, і це некрасиво, як на мене.

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

while( ( i > 0 ) && ( i < length() ) && isCorrect( element[ i ] ) )

бажано робити щось на кшталт

bool isInRange = true,
     mayContinue = isCorrect( element[ i ] );
while( mayContinue )
{
  ...
  isInRange = ( i > 0 ) && ( i < length() );
  mayContinue = isInRange && isCorrect( element[ i ] );
}

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

Хтось має якісь коментарі до цих фрагментів? Де я туплю?

2

Re: Оформлення кількох випадків циклів

Хтось має якісь коментарі до цих фрагментів? Де я туплю?

Не в тему, але я ніколи не думав, що почую такі слова від ВАС. До нас часом Галея не приближається?

Подякували: FakiNyan1

3

Re: Оформлення кількох випадків циклів

1. другий варіант більше подобається.
3. в розбитий на простіші чомусь значно довше заїжджав.

4

Re: Оформлення кількох випадків циклів

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

5

Re: Оформлення кількох випадків циклів

Не в тему, але я ніколи не думав, що почую такі слова від ВАС

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

    if(! X )
    {

    }
    while( X )
    {
    }

Зізнавайтесь, книжку пишите чи до лекції готуєтесь?

6

Re: Оформлення кількох випадків циклів

1. Чим поганий if всередині циклу? Починайте тролити, якщо що.
3. Складним умовам варто давати імена, якщо вони несуть якийсь сенс. Тобто у вашому прикладі isInRange — хороший приклад, а mayContinue — не дуже, він ні про що не говорить. Чисто моє imho.

Подякували: Arete1

7

Re: Оформлення кількох випадків циклів

1. Відповім цитатою з книги "Code complete" Steven McConnell. Досить цікавий варіант вирішення питання як мінімум для ознайомлення. Цитата немаленька тому сховав.

Цитата

Приклад дубльованого коду, схильного помилок при супроводі (C++)

// Обчислюємо рахунок і рейтинги.
score = 0;
// Ці рядки з'являються тут...
GetNextRating( &ratingIncrement );
rating = rating + ratingIncrement;
while ( ( score < targetScore ) && ( ratingIncrement != 0 ) ) {
  GetNextScore( &scoreIncrement );
  score = score + scoreIncrement;

  //...і повторюються тут.
  GetNextRating( &ratingIncrement );
  rating = rating + ratingIncrement;
}

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

Приклад циклу з виходом, легшого в супроводі (C++)

// Обчислюємо рахунок і рейтинги. Цей код використовує нескінченний цикл
// і оператор break для імітації циклу з виходом.
score = 0;
while ( true ) {
  GetNextRating( &ratingIncrement );
  rating = rating + ratingIncrement;
   // Це умова виходу з циклу (і тепер вона може бути спрощена за допомогою теорем ДеМоргана,
   // описаних в розділі 19.1).
  if ( !( ( score < targetScore ) && ( ratingIncrement != 0 ) ) ) {
    break;
  }
  GetNextScore( &scoreIncrement );
  score = score + scoreIncrement;
}

3. Стараюсь замінити складну умову на булеву функцію (чи кілька функцій) якщо функція несе в собі якийсь логічний зміст.

bool mayContinue ( int i ) {
  return
    ( i > 0 ) && ( i < length() ) && // in range
    isCorrect( element[ i ] ); // is correct
}

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

Подякували: Replace, 0xDADA11C72

8

Re: Оформлення кількох випадків циклів

FakiNyan написав:

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

Насправді круті прогери пишуть саме ясний, зрозумілий і прозорий код. Є таке правило: хитрий код = поганий код.

9

Re: Оформлення кількох випадків циклів

20x9111A: я невдало приклад обрав, треба з книги (див. нижче) скопіювати було.

20xDADA11C7: книжку читаю (див. нижче). А ваш варіант п.2 все ще лишає повторення умови в двох місцях, ну і там дуже проситься else стояти, тобто це тільки ускладнює картину.

2Arete: Ну от... справа в тому, що я якраз почав Макконнела читати (ага, я його досі не читав, мені соромно). 90% там мені, в принципі, вже і так відомо, але все ж треба знати класику :) Хоча і пару помилок в нього я вже знайшов  ]:->
1. Мені таке рішення не подобається з двох причин: по-перше, неструктурно, а по-друге (частково як наслідок) умова виходу схована всередині. Нічого, дістануся циклів в "Довершеному коді", може, щось клацне в голові...
3.  Це можливо, якщо всі умови залежать від однієї-двох змінних. Втім, якщо цикл залежить від десятка різних умов - то, дуже схоже, неправильно обрана абстракція і побудовані класи, які б переробили 2/3 з цих умов самі.

Подякували: Arete1

10

Re: Оформлення кількох випадків циклів

FakiNyan написав:

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

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

11

Re: Оформлення кількох випадків циклів

koala, Функція Paint() вміє обробити ситуацію, коли параметр - нульовий?

Якщо та, то можна зробити щось таке:

for( pin = firstPin, bar = firstBar; bar || pin; pin = pin.next(), bar = bar.next() )
{
  Paint( bar ); // if zero - just return
  Paint( pin ); // if zero - just return
}

12

Re: Оформлення кількох випадків циклів

Код з Макконнела зможете так переробити?

13

Re: Оформлення кількох випадків циклів

koala написав:

Код з Макконнела зможете так переробити?

Я Макконела не читав (лише трошки, може :)).
Що ви маєте на увазі?

14

Re: Оформлення кількох випадків циклів

Bartash написав:
koala написав:

Код з Макконнела зможете так переробити?

Я Макконела не читав (лише трошки, може :)).
Що ви маєте на увазі?

В Arete вище є цитата, там код складніше адекватно переробити.

15

Re: Оформлення кількох випадків циклів

koala написав:

В Arete вище є цитата, там код складніше адекватно переробити.

Так не варіант?

// somewhere above...
void modifyRating(int &rating, int &ratingInc)
{
    GetNextRating( &ratingInc );
    rating += ratingInc;
}

void modifyScore(int &score, int &scoreInc)
{
    GetNextScore( &scoreInc );
    score += scoreInct;
}

// Обчислюємо рахунок і рейтинги.

// Ці рядки з'являються тут...
for (score = 0, modifyRating(rating, ratingIncrement) ; ( score < targetScore ) && ratingIncrement ; modifyRating(rating, ratingIncrement) )
{
  modifyScore(score, scoreIncrement);
  //...
}

16 Востаннє редагувалося koala (24.09.2014 14:13:59)

Re: Оформлення кількох випадків циклів

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

Так, вже дістався до циклів із виходом в Макконнела. Це просто супер.

17

Re: Оформлення кількох випадків циклів

1. Можна придумати лаконічніші назви.
2. Тут не зрозумів.
3. Але ж блок, який виконується у переході та блоці ініціалізації, має виконуватися обов'язково? Чому б його там не тримати?

18

Re: Оформлення кількох випадків циклів

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