Esta é a maneira mais popular (me parece) de verificar se um valor está em uma matriz:
for (int x : array)
{
if (x == value)
return true;
}
return false;
No entanto, em um livro que li há muitos anos por, provavelmente, Wirth ou Dijkstra, foi dito que esse estilo é melhor (quando comparado a um loop while com uma saída interna):
int i = 0;
while (i < array.length && array[i] != value)
i++;
return i < array.length;
Dessa forma, a condição de saída adicional se torna uma parte explícita do invariante do loop, não há condições ocultas e sai dentro do loop, tudo é mais óbvio e mais de uma maneira de programação estruturada. Eu geralmente preferia esse último padrão sempre que possível e usei o for
loop para iterar apenas de a
para b
.
E, no entanto, não posso dizer que a primeira versão seja menos clara. Talvez seja ainda mais claro e fácil de entender, pelo menos para iniciantes. Então, eu ainda estou me perguntando qual é o melhor?
Talvez alguém possa dar uma boa justificativa em favor de um dos métodos?
Atualização: Não se trata de pontos de retorno de múltiplas funções, lambdas ou localização de um elemento em uma matriz em si. É sobre como escrever loops com invariantes mais complexos do que uma única desigualdade.
Atualização: OK, eu vejo o ponto das pessoas que respondem e comentam: eu misturei o loop foreach aqui, que já é muito mais claro e legível do que um loop while. Eu não deveria ter feito isso. Mas essa também é uma pergunta interessante, então vamos deixar como está: loop foreach e uma condição extra por dentro, ou um loop while com um loop explícito invariável e uma pós-condição depois. Parece que o loop foreach com uma condição e uma saída / interrupção está vencendo. Vou criar uma pergunta adicional sem o loop foreach (para uma lista vinculada).
fonte
collection.contains(foo)
Respostas:
Eu acho que para loops simples, como esses, a primeira sintaxe padrão é muito mais clara. Algumas pessoas consideram vários retornos confusos ou com cheiro de código, mas para um pedaço de código tão pequeno, não acredito que seja um problema real.
Fica um pouco mais discutível para loops mais complexos. Se o conteúdo do loop não puder caber na tela e apresentar vários retornos, é possível argumentar que os vários pontos de saída podem dificultar a manutenção do código. Por exemplo, se você tivesse que garantir que algum método de manutenção de estado fosse executado antes de sair da função, seria fácil deixar de adicioná-lo a uma das instruções de retorno e você causaria um erro. Se todas as condições finais puderem ser verificadas em um loop while, você terá apenas um ponto de saída e poderá adicionar esse código depois dele.
Dito isto, especialmente com loops, é bom tentar colocar o máximo de lógica possível em métodos separados. Isso evita muitos casos em que o segundo método teria vantagens. Loops enxutos com lógica claramente separada importarão mais do que qual desses estilos você usa. Além disso, se a maior parte da base de código do seu aplicativo estiver usando um estilo, você deve seguir esse estilo.
fonte
Isso é facil.
Quase nada importa mais do que clareza para o leitor. A primeira variante que achei incrivelmente simples e clara.
Na segunda versão 'aprimorada', tive que ler várias vezes e ter certeza de que todas as condições da borda estavam corretas.
Existe o ZERO DOUBT, que é o melhor estilo de codificação (o primeiro é muito melhor).
Agora - o que é CLARO para as pessoas pode variar de pessoa para pessoa. Não tenho certeza de que existem padrões objetivos para isso (embora postar em um fórum como esse e obter uma variedade de contribuições das pessoas possa ajudar).
Nesse caso em particular, no entanto, posso dizer por que o primeiro algoritmo é mais claro: sei como é a iteração C ++ na sintaxe de um contêiner. Eu o internalizei. Alguém UNFAMILIAR (sua nova sintaxe) com essa sintaxe pode preferir a segunda variação.
Mas uma vez que você conhece e entende essa nova sintaxe, é um conceito básico que você pode usar. Com a abordagem de iteração de loop (segunda), é necessário verificar cuidadosamente se o usuário está verificando CORRETAMENTE todas as condições de borda para fazer loop em toda a matriz (por exemplo, menos do que em vez do mesmo índice menor ou igual, usado para teste e para indexação etc).
fonte
longerLength = true
, entãoreturn longerLength
.length
. Se realmente fosse declarado como uma matriz e não um ponteiro, eles poderiam usarsizeof
ou, se fosse umastd::array
, a função de membro correta ésize()
, não há umalength
propriedade.sizeof
estaria em bytes ... O mais genérico desde C ++ 17 éstd::size()
.Nem tanto. A variável
i
existe fora do loop while aqui e, portanto, faz parte do escopo externo, enquanto (trocadilho intencional)x
dofor
loop existe apenas dentro do escopo do loop. O escopo é uma maneira muito importante de introduzir estrutura na programação.fonte
Os dois loops têm semânticas diferentes:
O primeiro loop simplesmente responde a uma pergunta simples de sim / não: "A matriz contém o objeto que estou procurando?" Fá-lo da maneira mais breve possível.
O segundo loop responde à pergunta: "Se a matriz contiver o objeto que estou procurando, qual é o índice da primeira correspondência?" Mais uma vez, faz isso da maneira mais breve possível.
Como a resposta para a segunda pergunta fornece estritamente mais informações do que a resposta para a primeira, você pode optar por responder à segunda e depois derivar a resposta da primeira. É o que a linha
return i < array.length;
faz, de qualquer maneira.Acredito que geralmente é melhor usar apenas a ferramenta adequada ao objetivo, a menos que você possa reutilizar uma ferramenta já existente e mais flexível. Ou seja:
bool
variável e quebra também é bom. (Evita a segundareturn
declaração, a resposta está disponível em uma variável em vez de em um retorno de função.)std::find
é bom (reutilização de código!).bool
não são.fonte
Vou sugerir uma terceira opção completamente:
Existem várias razões para iterar sobre uma matriz: verifique se existe um valor específico, transforme a matriz em outra matriz, calcule um valor agregado, filtre alguns valores da matriz ... Se você usar um loop for simples, não está claro rapidamente, especificamente como o loop for está sendo usado. No entanto, a maioria das linguagens modernas possui APIs ricas em suas estruturas de dados de matriz que tornam essas diferentes intenções muito explícitas.
Compare a transformação de uma matriz em outra com um loop for:
e usando uma
map
função no estilo JavaScript :Ou somando uma matriz:
versus:
Quanto tempo você leva para entender o que isso faz?
versus
Nos três casos, enquanto o loop for certamente é legível, você precisa dedicar alguns momentos para descobrir como o loop for está sendo usado e verificar se todos os contadores e condições de saída estão corretos. As funções modernas no estilo lambda tornam os objetivos dos loops extremamente explícitos e você sabe ao certo que as funções da API que estão sendo chamadas foram implementadas corretamente.
A maioria das linguagens modernas, incluindo JavaScript , Ruby , C # e Java , usa esse estilo de interação funcional com matrizes e coleções semelhantes.
Em geral, embora eu não ache que o uso de loops seja necessariamente errado e seja uma questão de gosto pessoal, tenho me sentido bastante favorável ao uso desse estilo de trabalhar com matrizes. Isto é especificamente devido à maior clareza na determinação do que cada loop está fazendo. Se seu idioma possui recursos ou ferramentas semelhantes em suas bibliotecas padrão, sugiro que você considere adotar esse estilo também!
fonte
array.find
sugere a pergunta, pois precisamos discutir a melhor maneira de implementararray.find
. A menos que você esteja usando hardware com umafind
operação integrada , precisamos escrever um loop lá.find
em suas bibliotecas padrão. Sem dúvida, essas bibliotecas implementamfind
e seus parentes usam loops, mas é isso que uma boa função faz: abstrai os detalhes técnicos do consumidor da função, permitindo que o programador não precise pensar nesses detalhes. Portanto, mesmo quefind
seja provavelmente implementado com um loop for, ele ainda ajuda a tornar o código mais legível e, como é frequente na biblioteca padrão, o uso dele não adiciona sobrecarga ou risco significativos.Tudo se resume exatamente ao que se entende por "melhor". Para programadores práticos, geralmente significa eficiente - ou seja, neste caso, sair diretamente do loop evita uma comparação extra e retornar uma constante booleana evita uma comparação duplicada; isso economiza ciclos. Dijkstra está mais preocupado em criar código que seja mais fácil de provar correto . [pareceu-me que a educação em CS na Europa leva o 'teste de correção de código' muito mais a sério do que a educação em CS nos EUA, onde as forças econômicas tendem a dominar a prática de codificação]
fonte