Никогда раньше не видел 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
@ Джесси Хорошо - Хороший код не имеет ничего общего с тем, что допустимо в языке, он имеет отношение к тому, сколько времени занимает неаквантованный сотрудник, чтобы прочитать код. Если это вызывает путаницу, это не самое лучшее решение, даже если оно законно. Часто более подробное решение намного лучше, чем краткое, и большинство компиляторов в любом случае будут компилировать одно и то же.
Билл К

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
@ Роджер Это не будет тот же цикл, так как вы уменьшаете u в конце цикла, а не в начале (то есть после b [u] = v вместо ранее). На самом деле вам нужно будет инициализировать его с помощью u = b.size() - 1.
Дидье Л

К вашему сведению: я только что столкнул вас с лимитом 500К. Это как быть где-то миллионным клиентом и хоть что-нибудь выиграть? В любом случае: много поздравлений; всегда с нетерпением до ваших точных, острых ответов! Продолжай; встретимся с вами, чтобы заработать 1 миллион штук ... позже в этом веке.
GhostCat

165

Много точных ответов, но я думаю, что стоит написать эквивалентный цикл while.

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 = b.size();
v = b.back();
while(u>0) {
   u--;
   b[u] = v;
   v = p[v];
}

Лучше быть ясным, чем кратким.


29
Этот ответ не только разъясняет плохой код, но и дает хорошую альтернативу. 1 вверх !!
полвоазул

2
Кроме того, я был бы осторожен с while (u-- >0)формой. Если интервал испорчен, вы можете закончить циклом «до нуля» while (u --> 0), который на первый взгляд может запутать всех. ( Я не уверен, действительно ли это C #, но это в C, и я думаю, что это может быть также и в C ++? )
Izkata

9
Я не думаю, что ваш код обязательно более понятен. Суть forвместо того, чтобы whileточно указать, что вы помещаете инициализацию и увеличение / уменьшение в одну инструкцию, и это не обязательно усложняет понимание кода. В противном случае, мы не должны использовать forвообще.
Musiphil

1
Также важно переписать как можно меньше кода. (В конце концов, цикл for может измениться.) Вы поставили «u--» в двух разных местах, и цикл не совсем понятен (я вижу, что цикл for делает одним взглядом; мне нужно сканировать с несколькими строками). Быть кратким также имеет свои преимущества. Не принижай их. Тем не менее, хороший пример того, как еще это можно было бы написать. Это облегчает понимание для тех, кто не привык к заявлениям в C ++ (или даже вообще).
NotKyon

2
@Izkata: Это НИКОГДА не оператор, он анализируется как --токен, за которым следует >токен. Два отдельных оператора. Цикл "до нуля" - это просто прямая комбинация пост-декремента и "больше, чем". Перегрузка операторов C ++ не создает новые операторы, а просто переназначает существующие.
Бен Фойгт

66

Условие u--;, потому что он находится во втором положении из за инструкции.

Если значение u--;отличается от 0, оно будет интерпретировано как true(то есть неявно приведено к логическому значению true). Если вместо этого его значение равно 0, оно будет приведено к false.

Это очень плохой код .

Обновление: я обсуждал написание цикла for в этом посте . Его рекомендации можно резюмировать в следующих параграфах:

Цикл for - это практичная, удобочитаемая (как только вы привыкнете к ней) и краткая конструкция, но вы должны хорошо ее использовать. Из-за его необычного синтаксиса, использование его слишком изобретательным способом не является хорошей идеей.

Все части цикла for должны быть короткими и удобочитаемыми. Имена переменных должны быть выбраны так, чтобы их было легко понять.

Этот пример явно нарушает эти рекомендации.


3
Или это остановится на u == 0, вероятно ...?
Томас

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

48
Это ужасный код по одной очень простой причине; Вы не могли легко понять это, когда вы читаете это. Это "умный", "взломать"; он использует комбинацию структур кодирования и знания о том, как они работают за кулисами, чтобы создать структуру, которая выполняет свою работу, но не поддается пониманию, потому что она не в той форме, которую предполагали авторы языка и которая была доведена до сведения большинства языковые пользователи.
KeithS

4
Отличный ответ. Я бы +1 это ... если бы не "Это очень плохой код". заявление;)
Sandman4

14
Я не понимаю, почему так много людей думают, что вы - действительно плохой код просто из-за отсутствия (неявного в 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;      
}

Просто замените эквивалент для size () и back ().

Что он делает, так это переворачивает список и сохраняет в массиве. Но в C # у нас напрямую есть определенная системой функция для этого. Таким образом, вам не нужно писать этот цикл также.

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

1
взяв v = b.back();из за intializer не просто изменить, как она работает, так как v = p[v]перегружается
Жоау Portela

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

все ответы верны: -

Цикл for может использоваться различными способами:

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 # как

u--! = 0

или

u--> 0

Но все же старайтесь избегать такого рода практики кодирования.

в то время как цикл

Изложенное выше в ответе является одной из самых упрощенных версий вашего

для цикла.


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

3

Если вы привыкли к C / C ++, этот код не так сложен для чтения, хотя он довольно лаконичен и не так хорош для кода. Итак, позвольте мне объяснить части, которые больше Cism, чем что-либо еще. Прежде всего, общий синтаксис цикла C for выглядит следующим образом:

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 выполняет свою работу и ничего больше. В исходном коде условие модифицировало переменную. Часть приращения делала то, что должно быть в блоке кода и т. Д.

Оператор запятой также может привести вас к циклу. В 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.