Ніколи не бачив C ++ для циклу


164

Я перетворював алгоритм C ++ в C #. Я натрапив на це для циклу:

for (u = b.size(), v = b.back(); u--; v = p[v]) 
b[u] = v;

Він не дає помилок у C ++, але він робить у C # (не може перетворити int в bool). Я справді не можу розібратися в цьому циклі, де умова?

Може хтось, будь ласка, пояснить?

PS. Просто для перевірки, чи адаптувати ВЕКТОР до списку, чи відповідає b.back () b [b.Count-1]?


35
Де стан? Це було б u--. Напівколонки використовуються для розмежування різних частин forвисловлювання.
Девід Хеффернан

77
Це цілком нормальна петля. C # не перетворює числа в bools неявно, тому вам потрібно перетворити умову на; u-- != 0;
R. Martinho Fernandes

28
@Jessie Good - Хороший код не має нічого спільного з тим, що дозволено в мові, він має відношення до того, скільки часу потрібен недоброзичливий співробітник, щоб прочитати код. Якщо це викликає будь-яку плутанину, це не найкраще можливе рішення, навіть якщо це законно. Часто більш багатослівне рішення набагато краще, ніж короткий, і більшість компіляторів будуть компілювати одне й те саме.
Білл К

18
Я сподіваюся , що після перетворення коду, ви даєте змінні краще , ніж імена b, u, vі т.д. Єдина причина , чому вони були названі так тому , що хто - то хотів , щоб виглядати розумним, роблячи їх код нечитабельним.
День

33
@houbysoft: це загальна проблема стаціонарного потоку. Якщо ви задасте дуже детальне, добре вивчене та цікаве питання у конкретній галузі дослідження, що призводить до вирішення важкої та цікавої проблеми, а ви відповісте на таке запитання після важких днів, що варті дослідження, ви отримаєте лише декілька десяток відвідувачів та один-два відгуки від кількох експертів у цій галузі. Якщо ви хочете швидко отримати багато представників, вам доведеться задавати відповіді на такі питання. "Як додати два числа в php", "що doозначає C ++" - отримають тисячі звернень від початківців, які шукають підручник.
vsz

Відповіді:


320

Стан forпетлі знаходиться посередині - між двома крапками з комою ;.

У C ++ нормально ставити майже будь-який вираз як умову: все, що оцінюється до нуля, означає false; ненульові засоби true.

У вашому випадку умовою є u--: коли ви переходите на C #, просто додайте != 0:

for (u = b.size(), v = b.back(); u-- != 0; v = p[v]) 
    b[u] = v; //                     ^^^^ HERE

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

2
Якщо я пам'ятаю правильно, а вираз, що містить кому, має значення останнього підвырази справа. Це походить від C і може бути використане в будь-якому виразі, не тільки в циклі for.
Джорджіо

7
@Roger Це був би не той самий цикл, оскільки ви декрементуєте u в кінці циклу, а не на початку (тобто після b [u] = v замість раніше). Насправді вам потрібно буде ініціалізувати це u = b.size() - 1замість цього.
Didier L

Фій: Я просто штовхнув тебе за ліміт 500K. Це як бути мільйонним клієнтом десь і вигравати щось? У будь-якому випадку: багато привітань; завжди дивлячись на ваші точні, гострі відповіді! Продовжуй; познайомимося, зробивши річ на мільйон ... пізніше цього століття.
GhostCat

165

Багато точних відповідей, але я вважаю, що варто виписати еквівалентний цикл.

for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

Еквівалентний:

u = b.size();
v = b.back();
while(u--) {
   b[u] = v;
   v = p[v];
}

Ви можете розглянути можливість рефакторингу на формат while () під час перекладу на C #. На мою думку, це зрозуміліше, менше пастки для нових програмістів і однаково ефективно.

Як уже зазначалося , - але , щоб зробити мою відповідь повний - щоб змусити його працювати в C # ви повинні зміни while(u--)в while(u-- != 0).

... або про while(u-- >0)всяк випадок, коли ви почнете негативною. (Гаразд, b.size()ніколи не буде негативним - але розглянемо загальний випадок, коли можливо щось інше ініціалізується u).

Або, щоб зробити це ще зрозумілішим:

u = b.size();
v = b.back();
while(u>0) {
   u--;
   b[u] = v;
   v = p[v];
}

Краще бути зрозумілим, ніж бути лайливим.


29
Ця відповідь не тільки роз'яснює поганий код, але й дає хорошу альтернативу. 1 вгору !!
polvoazul

2
Як осторонь, я буду обережним із while (u-- >0)формою. Якщо інтервал буде зіпсований, ви можете закінчити цикл "до нуля":, while (u --> 0)який, як правило, збиває з пантелику всіх. ( Я не впевнений, чи дійсний це C #, але він є в C, і я думаю, що це може бути так само і в C ++? )
Izkata

9
Я не думаю, що ваш код обов'язково зрозуміліший. Сенс forзамість цього whileполягає саме в тому, що ви ставите ініціалізацію та приріст / декремент в одне твердження, і це не обов'язково ускладнює розуміння коду. Інакше ми взагалі не повинні користуватися for.
musiphil

1
Також важливо переписати якомога менше коду. (Зрештою, цикл for може змінитися.) Ви поставили "u--" у два окремі місця, і цикл не дуже чіткий (я бачу, що робить цикл одним поглядом; мені потрібно сканувати з декількома рядками). Бути ласим має і переваги. Не применшуйте їх. І все-таки гарний приклад того, як ще можна було написати. Це полегшує розуміння для тих, хто не звик до висловлювань на C ++ (а може, навіть і зовсім).
NotKyon

2
@Izkata: Це НІКОЛИ не оператор, він розбирається як --маркер, за яким слідує >маркер. Два окремих оператора. Цикл "вниз до нуля" - це просто пряма комбінація після декретації та більше ніж. Перевантаження C ++ оператора не створює нових операторів, воно просто замінює існуючі.
Бен Войгт

66

Умова така u--; , тому що він знаходиться у другій позиції з за інструкції.

Якщо значення u--; відрізняється від 0, воно буде трактуватися як true(тобто неявно відведене до булевого значення true). Якщо натомість його значення дорівнює 0, воно буде враховано false.

Це дуже поганий код .

Оновлення. У цій публікації блогу я обговорював написання циклів "для" . Його рекомендації можна узагальнити у наступних параграфах:

Цикл для циклу - це практичний, читабельний (як тільки ви звикнете до нього) та лаконічна конструкція, але вам потрібно добре його використовувати. Через незвичайний синтаксис, використовувати його занадто образно, це не дуже гарна ідея.

Всі частини петлі for повинні бути короткими і читабельними. Слід вибирати назви змінних, щоб їх було легко зрозуміти.

Цей приклад явно порушує ці рекомендації.


3
Або зупиниться на u == 0, ймовірно, ...?
Томас

2
Немає; в C ++ є неявна конверсія від int до bool, з 0 перетворенням у false. Цикл закінчується, коли u-- == 0. У C # немає такого неявного перетворення, тому вам доведеться прямо сказати u-- == 0. EDIT: Це у відповідь на ваш перший коментар.
Кріс

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

4
Відмінна відповідь. Я поставив би +1 цьому ... якби не "Це дуже поганий код". заява;)
Sandman4

14
Я не розумію , чому так багато людей , здається, думають u-- дійсно поганий код просто з - за якої бракує (неявний в C ++) ! = 0 . Безумовно, кожен, хто працює з кодом, буде чудово усвідомлювати, що 0 = false, кожне інше значення = true . Існує набагато більше можливостей для плутанини щодо попереднього / після нарощування u , або люди, можливо, припускаючи, що u = b.size () завжди буде виконуватися перед v = b.back () (моє розуміння, послідовність виконання там не визначена, але я витримую виправлення).
FumbleFingers

23

Це буде форма C # вашого циклу.

// back fetches the last element of vector in c++.
for (u = b.size(), v = b.back(); (u--) != 0; v = p[v]) 
{      
  b[u] = v;      
}

Просто замініть еквівалент за розміром () і назад ().

Що це робить, це повертає список і зберігає масив. Але в C # у нас є безпосередньо визначена для цього функція системи. Тому вам також не потрібно писати цю петлю.

b = b.Reverse().ToArray();

1
виймаючи v = b.back();інструмент для інтилятора, ви не просто змінили спосіб його роботи, оскільки його v = p[v]перекреслив
Жоао Портела,

v = p [v] не матиме жодного впливу на кінцевий результат, оскільки цей рядок буде виконуватися останнім часом. І після цього v не називається циклом. Цей рядок є лише для того, щоб показати, як цикл перетворюється з c ++ в C #.
Нарендра

1
в коді c ++ v = b.back();виконується один раз до початку ітерацій і v = p[v]виконується на початку кожної ітерації. У цій версії C # v = p[v]все ще виконується на початку кожної ітерації, але v = b.back();виконується відразу після неї, змінюючи значення vдля наступної інструкції b[u] = v;. (можливо, питання було відредаговано після того, як ви його прочитали)
Жоао Портела,

5
@Rain Проблема є v = b.back(). У вас це виконується при кожній ітерації циклу, а не лише в першій - ми не знаємо, що back()робить (чи є побічні ефекти? Чи це змінює внутрішнє представлення b?), Тому цей цикл не є еквівалентним такому в питання.
Ізката

1
@Rain Точнісінько, уважно подивіться на це самі. Ініціалізації крок відбувається тільки один раз перед початком циклу, а НЕ на початку кожної ітерації . Ваш код був би правильним, якби він v = b.back()був переміщений за межами циклу, над ним. (Крім того, якщо ви намагаєтесь відповісти на когось, використовуйте @перед їх іменем, щоб ми отримали сповіщення)
Izkata


14

Умова є результатом u--, який є значенням uдо зменшення.

В C і C ++, int є конвертованим в BOOL по неявному робить != 0порівняння (0 є false, все інше true).

b.back()є останнім елементом контейнера, який є b[b.size() - 1], коли size() != 0.


11

У C все не нульове значення знаходиться trueв "булевих" контекстах, таких як умова кінця циклу або умовне твердження. У C # ви повинні зробити це перевірити явним: u-- != 0.


Це не питання ОП. Він запитує про оцінку термінального стану (тобто "u--" в даному випадку).
ApplePie

6

Як стверджують інші, той факт, що C ++ має неявний викид на булевий, означає умовне u--, що буде істинним, якщо значення не дорівнює нулю.

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

for(int i = 0; ; ++i)
  doThisForever(i);

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

Загалом for(A; B; C){D}або for(A; B; C)D;стає:

{A}
loopBack:
if(!(B))
  goto escapeLoop;
{D}
{C}
goto loopBack;
escapeLoop:

Будь-який або більше A, B, C або D можна залишити.

Внаслідок цього деяка прихильність for(;;) до нескінченних петель. Я це роблю, тому що, хоча він while(true)є більш популярним, я читаю це як "поки істина не закінчиться правдою", що звучить дещо апокаліптично порівняно з моїм читанням for(;;) як "назавжди".

Це питання смаку, але оскільки я не єдина людина у світі, яка подобається for(;;), варто знати, що це означає.


4

всі відповіді правильні: -

петлю можна використовувати різними способами наступним чином:

Single Statement inside For Loop
Multiple Statements inside For Loop
No Statement inside For Loop
Semicolon at the end of For Loop
Multiple Initialization Statement inside For
Missing Initialization in For Loop
Missing Increment/Decrement Statement
Infinite For Loop
Condition with no Conditional Operator.

4
for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

У коді вище, uі vне започатковано b.size()і b.back().

Кожен раз, коли умова перевіряється, вона також виконує заяву про зменшення, тобто u--.

forЦикл буде виходити , коли uбуде 0.


3

Помилка, що виникає в C #, сама знімає сумніви. Шукає цикл for

ПОМИЛКОВИЙ

умова припинити. І як ми знаємо,

(BOOL) FALSE = (int) 0

але C # не може обробити це самостійно на відміну від C ++. Тож умова, яку ви шукаєте, така

u--

але ви повинні чітко дати умову в C # as

u--! = 0

або

u--> 0

Але все ж намагайтеся уникати подібної практики кодування. The

поки петля

сказане вище у відповіді - це одна з найбільш спрощених ваших версій

for-петля.


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

3

Якщо ви звикли до C / C ++, цей код не так важко читати, хоча він досить короткий і не дуже великий. Тож дозвольте мені пояснити частини, які є більшою за Cism ніж будь-що інше. Спочатку загальний синтаксис C для циклу виглядає так:

for (<initialization> ; <condition>; <increment>)
{
    <code...>
}

Код ініціалізації запускається один раз. Потім умова перевіряється перед кожною петлею, і нарешті, приріст стає викликом після кожного циклу. Тож у вашому прикладі ви знайдете умовуu--

Чому u--робота працює як умова в C, а не C #? Оскільки C неявно перетворює багато речей, які занадто сильно розгортаються, і це може спричинити неприємності. Для числа будь-що, що не є нулем, є істинним, а нуль - хибним. Таким чином, він буде рахувати від b.size () - 1 до 0. Наявність побічного ефекту в умові трохи дратує, і було б краще помістити його в прирістну частину циклу for, хоча багато C код робить це. Якби я писав це, я зробив би це так:

for (u = b.size() - 1, v = b.back(); u>=0; --u) 
{
    b[u] = v;
    v = p[v]
}

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

Оператор з комами може також кидати вас за цикл. У C щось на зразок x=1,y=2виглядає як одне твердження, що стосується компілятора, і вписується в код ініціалізації. Він просто оцінює кожну з частин і повертає значення останньої. Так, наприклад:

std::cout << "(1,2)=" << (1,2) << std::endl;

буде надруковано 2.


Проблема з вашим перезаписом полягає в тому, що якщо b.size () не підписано, воно буде повторюватися дуже довго. (Крім того, вам не вистачає крапки з комою.) Але принаймні ви не сприйняли підхід "Дік і Джейн" - це хороша англійська мова ", що так багато інших відповідей і коментарів зробили, похвалившись безглуздо багатослівними переписуваннями, які тільки простіші читати неофітами та іншими некваліфікованими програмістами.
Джим Балтер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.