Nunca visto antes em C ++ para loop

164

Eu estava convertendo um algoritmo C ++ para c #. Me deparei com isso para loop:

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

Ele não fornece erro em C ++, mas em C # (não é possível converter int em bool). Eu realmente não consigo descobrir isso para loop, onde está a condição?

Alguém pode me explicar?

PS. Apenas para verificar, para adaptar um VETOR a uma LIST, b.back () corresponde a b [b.Count-1]?

Thomas
fonte
35
Onde está a condição? Isso seria u--. Os pontos-e-vírgulas são usados ​​para delimitar as várias partes da fordeclaração.
David Heffernan
77
Este é um loop bastante normal. C # não converte números para bools implicitamente assim que você precisa para transformar a condição em; u-- != 0;
R. Martinho Fernandes
28
@ Jessie Good - Um bom código não tem nada a ver com o que é permitido no idioma, mas com o tempo que um colega de trabalho não autorizado leva para ler o código. Se causar algum tipo de confusão, não é a melhor solução possível, mesmo que seja legal. Freqüentemente, uma solução mais detalhada é muito melhor que uma solução concisa, e a maioria dos compiladores compila da mesma maneira.
Bill K
18
Espero que, depois de converter o código, você dá as variáveis nomes melhores do que b, u, v, etc. A única razão pela qual eles foram nomeados dessa maneira é porque alguém queria olhar inteligente, fazendo seu código ilegível.
Dan
33
@houbysoft: este é um problema geral de stackoverflow. Se você fizer uma pergunta muito detalhada, bem pesquisada e interessante em um domínio de pesquisa específico, o que leva a uma solução para um problema difícil e interessante, e você responder a essa pergunta após dias difíceis de pesquisa, receberá apenas alguns uma dúzia de visitantes e um ou dois votos positivos de alguns especialistas da área. Se você deseja obter muitos representantes rapidamente, é necessário fazer e responder a perguntas como estas. "Como adiciono dois números no php", "o que dosignifica em C ++" - receberá milhares de acessos de iniciantes à procura de um tutorial.
vsz

Respostas:

320

A condição do forloop está no meio - entre os dois ponto e vírgula ;.

Em C ++, não há problema em colocar quase qualquer expressão como condição: qualquer coisa que avalie como zero significa false; meios diferentes de zero true.

No seu caso, a condição é u--: quando você converter para C #, basta adicionar != 0:

for (u = b.size(), v = b.back(); u-- != 0; v = p[v]) 
    b[u] = v; //                     ^^^^ HERE
dasblinkenlight
fonte
55
A propósito, Thomas pode ter ficado confuso com o uso da vírgula também, é muito diferente do ponto e vírgula, permite fazer várias coisas em uma seção do loop for (nesse caso, inicializou duas variáveis). A última vez que verifiquei essas construções incomuns não era considerada a solução mais legível possível e, portanto, pode ser desaprovada por alguns.
Bill K
2
Se bem me lembro, e a expressão que contém uma vírgula tem o valor da última subexpressão à direita. Isso vem de C e pode ser usado em qualquer expressão, não apenas em um loop for.
Giorgio
7
@ Roger Este não seria o mesmo loop, pois você está diminuindo u no final do loop, em vez do início (ou seja, depois de b [u] = v em vez de antes). De fato, você precisaria inicializá-lo u = b.size() - 1.
Didier L
Fyi: Eu acabei de ultrapassar o limite de 500 mil. É como ser o milionésimo cliente em algum lugar e ganhar alguma coisa? De qualquer forma: muitos parabéns; sempre olhando para suas respostas precisas e nítidas! Continue; encontro você fazendo a coisa de 1 milhão ... mais adiante neste século.
GhostCat
165

Muitas respostas precisas, mas acho que vale a pena escrever o equivalente while.

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

É equivalente a:

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

Você pode refatorar para o formato while () ao traduzir para C #. Na minha opinião, é mais claro, menos uma armadilha para novos programadores e igualmente eficiente.

Como outros já apontaram - mas para completar minha resposta - para que funcione em C #, você precisaria mudar while(u--)para while(u-- != 0).

... ou while(u-- >0)apenas no caso de você começar negativo. (OK, b.size()nunca será negativo - mas considere um caso geral em que talvez algo mais tenha sido inicializado em u).

Ou, para deixar ainda mais claro:

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

É melhor ser claro do que ser conciso.

fino
fonte
29
Essa resposta não apenas esclarece o código incorreto, mas também fornece uma boa alternativa. 1 para cima!!
Polvoazul 31/07/2012
2
Como um aparte, eu teria cuidado com o while (u-- >0)formulário. Se o espaçamento for prejudicado, você poderá acabar com um loop "de baixo a zero":, while (u --> 0)que tende a confundir todos à primeira vista. ( Não tenho certeza se é válido C #, mas é em C, e eu acho que pode ser bem em C ++? )
Izkata
9
Não acho que seu código seja necessariamente mais claro. O objetivo de em forvez de whileé precisamente que você coloque a inicialização e o incremento / decremento em uma instrução, e isso não necessariamente torna o código mais difícil de entender. Caso contrário, não deveríamos estar usando for.
31412 musiphil
1
Também é importante reescrever o mínimo de código possível. (O loop for pode mudar, afinal.) Você colocou "u--" em dois lugares separados, e o loop não é realmente mais claro (posso ver o que o loop for faz de uma só vez; preciso escanear com várias linhas). Ser conciso também traz benefícios. Não subestime eles. Ainda assim, um bom exemplo de como mais poderia ser escrito. Isso facilita a compreensão para quem não está acostumado a fazer declarações em C ++ (ou talvez até mesmo).
NotKyon
2
@ Izkata: NUNCA é um operador, é analisado como um --token seguido por um >token. Dois operadores separados. Um loop "até zero" é apenas uma combinação direta de pós-decremento e maior que. A sobrecarga de operadores em C ++ não cria novos operadores, apenas redireciona os existentes.
Ben Voigt
66

A condição é u--;, porque está na segunda posição da instrução for .

Se o valor de u--;for diferente de 0, será interpretado como true(ou seja, convertido implicitamente no valor booleano true). Se, em vez disso, seu valor for 0, ele será convertido em false.

Este é um código muito ruim .

Atualização: discuti a redação dos loops "for" nesta postagem do blog . Suas recomendações podem ser resumidas nos seguintes parágrafos:

Um loop for é um construto prático, legível (quando você se acostumar) e conciso, mas você precisa usá-lo bem. Por causa de sua sintaxe incomum, usá-lo de maneira muito imaginativa não é uma boa idéia.

Todas as partes do loop for devem ser curtas e legíveis. Nomes de variáveis ​​devem ser escolhidos para facilitar o entendimento.

Este exemplo viola claramente essas recomendações.

Daniel Daranas
fonte
3
Ou ele irá parar em u == 0 provavelmente ...?
Thomas
2
Não; em C ++ há uma conversão implícita de int para bool, com 0 convertendo em false. O loop será encerrado quando u-- == 0. No C #, não existe uma conversão implícita, então você teria que dizer explicitamente u-- == 0. EDIT: Isso ocorre em resposta ao seu primeiro comentário.
Chris
48
Este é um código terrível , por uma razão muito simples; você não podia compreender facilmente quando o lê. É "inteligente", um "hack"; utiliza uma combinação de estruturas de codificação e conhecimento de como elas funcionam nos bastidores, para criar uma estrutura que faz o trabalho, mas que desafia a compreensão, porque não é da forma que os autores da linguagem imaginaram e que foi comunicada à maioria usuários de idiomas.
31412 KeithS
4
Excelente resposta. Eu marcaria com +1 ... se não o código "Este é um código muito ruim". declaração;)
Sandman4
14
Eu não entendo por que tantas pessoas parecem pensar que u-- é realmente um código ruim simplesmente por causa da falta (implícita em C ++) ! = 0 . Certamente quem trabalha com código estará perfeitamente ciente de que 0 = false, todos os outros valores = true . Há muito mais espaço para confusão com relação ao incremento pré / pós- u , ou talvez as pessoas suponham que u = b.size () sempre será executado antes de v = b.back () (meu entendimento é a sequência de execução indefinida, mas eu tenho que ser corrigido).
FumbleFingers
23

Esse será o formato C # do seu loop.

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

Apenas substitua o equivalente por size () e back ().

O que ele faz é reverter a lista e armazenar em uma matriz. Mas em C #, temos diretamente uma função definida pelo sistema para isso. Então você não precisa escrever esse loop também.

b = b.Reverse().ToArray();
Narendra
fonte
1
tomando v = b.back();para fora do para intializer não basta mudar a forma como ele funciona, uma vez que o v = p[v]é substituído por
João Portela
v = p [v] não terá efeito no resultado final, pois esta linha será executada no final. E depois disso v não é referido no loop. Essa linha existe apenas para mostrar como o loop é convertido de c ++ para c #.
Narendra 31/07
1
no código c ++ v = b.back();foi executado uma vez antes do início das iterações e v = p[v]foi executado no início de cada iteração. Nessa versão C #, v = p[v]ainda é executado no início de cada iteração, mas v = b.back();é executado logo após, alterando o valor da vpróxima instrução b[u] = v;. (talvez a pergunta tenha sido editada depois que você a leu)
João Portela
5
@Rain A questão é v = b.back(). Você o executa em todas as iterações do loop, e não apenas no primeiro - não sabemos o que back()faz (existem efeitos colaterais? Altera a representação interna de b?), Portanto esse loop não é equivalente ao do a questão.
Izkata 31/07/12
1
@Rain Exatamente, dê uma olhada mais de perto. A etapa de inicialização ocorre apenas uma vez antes do início do loop, não no início de cada iteração . Seu código estaria correto se v = b.back()fosse movido para fora do loop, acima dele. (Além disso, se você está tentando responder a alguém, use @na frente de seu nome, então temos uma notificação)
Izkata
15
u = b.size(), v = b.back()

é inicialização.

u--

é a condição.

v = p[v]

é a iteração

huseyin tugrul buyukisik
fonte
14

A condição é o resultado de u--, que é o valor de uantes de ser diminuído.

Em C e C ++, um int é convertível em booleano implicitamente fazendo uma != 0comparação (0 é false, tudo o resto é true).

b.back()é o último elemento em um contêiner, ou seja b[b.size() - 1], quando size() != 0.

Bo Persson
fonte
11

Em C, tudo que não é zero está trueem contextos "booleanos", como a condição final do loop ou uma instrução condicional. Em C # você tem que fazer essa verificação explícita: u-- != 0.

Joey
fonte
Esta não é a questão do OP. Ele está perguntando sobre a avaliação da condição terminal (ou seja, 'u--' neste caso).
ApplePie
6

Conforme declarado por outros, o fato de o C ++ ter implícito a conversão para booleano significa que o condicional é u--, o que será verdadeiro se o valor for diferente de zero.

Vale acrescentar que você tem uma falsa suposição ao perguntar "onde está o condicional". No C ++ e no C # (e em outras linguagens com sintaxe semelhante), você pode ter uma condicional vazia. Neste caso, sempre avaliada como verdadeira, então o circuito continua para sempre, ou até algumas outras saídas condicioná-lo (via return, breakou throw).

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

De fato, qualquer parte da instrução for pode ser deixada de fora, caso em que simplesmente não é executada.

Em geral, for(A; B; C){D}ou for(A; B; C)D;se torna:

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

Qualquer um ou mais de A, B, C ou D pode ser deixado de fora.

Como resultado disso, algum favor for(;;) para loops infinitos. Faço isso porque, embora while(true)seja mais popular, li isso como "até que a verdade acabe sendo verdadeira", o que soa um pouco apocalíptico em comparação com a minha leitura for(;;) como "para sempre".

É uma questão de gosto, mas como não sou a única pessoa no mundo a gostar for(;;), vale a pena saber o que isso significa.

Jon Hanna
fonte
4

todas as respostas estão corretas: -

O loop for pode ser usado de várias maneiras, como a seguir:

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.
Birubisht
fonte
4
for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

No código acima, ue vsão inicializados com b.size()e b.back().

Toda vez que a condição é verificada, ela também executa a instrução de decremento, ou seja u--.

O forloop sairá quando use tornar 0.

Apte
fonte
3

O erro encontrado no próprio C # limpa a dúvida. O loop for procura um

FALSO

condição para terminar. E como sabemos,

(BOOL) FALSE = (int) 0

mas o C # não pode processar isso sozinho, diferentemente do C ++. Portanto, a condição que você está procurando é

você--

mas você deve fornecer explicitamente a condição em C # como

u--! = 0

ou

u--> 0

Mas ainda tente evitar esse tipo de prática de codificação. o

enquanto loop

indicado acima em resposta é uma das versões mais simplificadas do seu

for-loop.

Abhineet
fonte
@ Downowner: O downvoting é bom na medida em que você não está satisfeito com a solução, mas, ao mesmo tempo, reserve um tempo para indicar o motivo, para que as respostas possam ser melhoradas.
Abhineet
3

Se você está acostumado a C / C ++, esse código não é tão difícil de ler, embora seja bastante conciso e não muito bom. Então, deixe-me explicar as partes que são mais Cism do que qualquer outra coisa. Primeiro, a sintaxe geral de um loop C for assim:

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

O código de inicialização é executado uma vez. Em seguida, a condição é testada antes de cada loop e, por fim, o incremento é chamado após cada loop. Então, no seu exemplo, você encontrará a condiçãou--

Por que u--funciona como uma condição em C e não em C #? Porque C converte implicitamente muitas coisas em bools e isso pode causar problemas. Para um número, qualquer coisa que não seja zero é verdadeira e zero é falso. Portanto, ele fará a contagem regressiva de b.size () - 1 a 0. Ter o efeito colateral na condição é um pouco chato e seria preferível colocá-lo na parte de incremento do loop for, embora muito C código faz isso. Se eu estivesse escrevendo, faria mais assim:

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

A razão para isso é, pelo menos para mim, é mais claro. Cada parte do loop for faz seu trabalho e nada mais. No código original, a condição estava modificando a variável. A parte do incremento estava fazendo algo que deveria estar no bloco de código etc.

O operador de vírgula também pode estar jogando você para um loop. Em C, algo parecido x=1,y=2com uma declaração no que diz respeito ao compilador e se encaixa no código de inicialização. Apenas avalia cada uma das partes e retorna o valor da última. Então, por exemplo:

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

imprimiria 2.

Matt Price
fonte
O problema com sua reescrita é que, se b.size () não estiver assinado, ele será repetido por um período muito longo. (Além disso, falta um ponto-e-vírgula.) Mas pelo menos você não adotou a abordagem "'Dick and Jane' é um bom inglês" que muitas das outras respostas e comentários fizeram, elogiando reescrições absurdamente verbais que são apenas mais fáceis para ler por neófitos e outros programadores não qualificados.
Jim Balter