Preciso retroceder em uma matriz, então tenho um código como este:
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something
myArray[i] = 42;
}
Existe um jeito melhor de fazer isso?
Atualização: eu esperava que talvez C # tivesse algum mecanismo integrado para isso, como:
foreachbackwards (int i in myArray)
{
// so easy
}
Update 2: Não são as melhores formas. Rune leva o prêmio com:
for (int i = myArray.Length; i-- > 0; )
{
//do something
}
//or
for (int i = myArray.Length; i --> 0; )
{
// do something
}
que fica ainda melhor em C normal (graças a Twotymz):
for (int i = lengthOfArray; i--; )
{
//do something
}
Respostas:
Embora seja um pouco obscuro, eu diria que a maneira mais agradável tipograficamente de fazer isso é
fonte
Em C ++, você basicamente tem a escolha entre iterar usando iteradores ou índices. Dependendo se você tem um array simples ou um
std::vector
, você usa técnicas diferentes.Usando std :: vector
Usando iteradores
C ++ permite que você faça isso usando
std::reverse_iterator:
Usando índices
O tipo integral sem sinal retornado por
std::vector<T>::size
é não semprestd::size_t
. Pode ser maior ou menor. Isso é crucial para o loop funcionar.Funciona, pois os valores dos tipos inteiros sem sinal são definidos por meio do módulo de sua contagem de bits. Assim, se você está configurando
-N
, você acaba em(2 ^ BIT_SIZE) -N
Usando matrizes
Usando iteradores
Estamos usando
std::reverse_iterator
para fazer a iteração.Usando índices
Podemos usar com segurança
std::size_t
aqui, ao contrário de acima, poissizeof
sempre retornastd::size_t
por definição.Evitando armadilhas com sizeof aplicado a ponteiros
Na verdade, a maneira acima de determinar o tamanho de uma matriz é uma droga. Se a for na verdade um ponteiro em vez de um array (o que acontece com frequência e os iniciantes vão confundi-lo), ele falhará silenciosamente. A melhor maneira é usar o seguinte, que falhará no tempo de compilação, se for fornecido um ponteiro:
Ele funciona obtendo o tamanho do array passado primeiro e, em seguida, declarando retornar uma referência a um array do tipo char do mesmo tamanho.
char
é definido para tersizeof
de: 1. Portanto, o array retornado terá umsizeof
de: N * 1, que é o que estamos procurando, com apenas avaliação de tempo de compilação e zero overhead de tempo de execução.Em vez de fazer
Mude seu código para que agora
fonte
end
ebegin
na ordem inversa?Em C # , usando o Visual Studio 2005 ou posterior, digite 'forr' e pressione [TAB] [TAB] . Isso se expandirá para um
for
loop que vai para trás em uma coleção.É tão fácil errar (pelo menos para mim), que pensei que seria uma boa ideia inserir este trecho.
Dito isso, eu gosto de
Array.Reverse()
/Enumerable.Reverse()
e itero melhor para frente - eles declaram a intenção com mais clareza.fonte
Eu sempre preferiria código claro em vez de código ' tipograficamente agradável '. Portanto, eu sempre usaria:
Você pode considerá-lo a maneira padrão de fazer um loop para trás.
Apenas meus dois centavos...
fonte
Em C # usando Linq :
fonte
Essa é definitivamente a melhor maneira para qualquer array cujo comprimento seja um tipo integral assinado. Para matrizes cujos comprimentos são de um tipo integral sem sinal (por exemplo,
std::vector
em C ++), você precisa modificar ligeiramente a condição final:Se você acabou de dizer
i >= 0
, isso sempre é verdade para um inteiro sem sinal, então o loop será um loop infinito.fonte
Parece bom para mim. Se o indexador não tiver assinatura (uint etc), talvez seja necessário levar isso em consideração. Chame-me de preguiçoso, mas nesse caso (sem sinal), posso apenas usar uma contra-variável:
(na verdade, mesmo aqui, você precisa ter cuidado com casos como arr.Length = uint.MaxValue ... talvez a! = em algum lugar ... claro, esse é um caso muito improvável!)
fonte
Em CI gosto de fazer isso:
Exemplo C # adicionado por MusiGenesis:
fonte
A melhor maneira de fazer isso em C ++ é provavelmente usar adaptadores iteradores (ou melhor, de faixa), que transformarão preguiçosamente a sequência à medida que ela é percorrida.
Basicamente,
Exibe o intervalo "intervalo" (aqui, está vazio, mas tenho quase certeza de que você mesmo pode adicionar elementos) na ordem inversa. É claro que simplesmente iterar o intervalo não é muito útil, mas passar esse novo intervalo para algoritmos e outras coisas é muito legal.
Este mecanismo também pode ser usado para usos muito mais poderosos:
Calculará preguiçosamente o intervalo "intervalo", onde a função "f" é aplicada a todos os elementos, os elementos para os quais "p" não é verdadeiro são removidos e, finalmente, o intervalo resultante é invertido.
A sintaxe do pipe é a IMO mais legível, devido ao seu infixo. A atualização da biblioteca Boost.Range com revisão pendente implementa isso, mas é muito simples de fazer você mesmo também. É ainda mais legal com um lambda DSEL gerar a função f e o predicado p em linha.
fonte
fonte
Eu prefiro um loop while. É mais claro para mim do que diminuir
i
a condição de um loop forfonte
Eu usaria o código da pergunta original, mas se você realmente quisesse usar foreach e ter um índice inteiro em C #:
fonte
Vou tentar responder minha própria pergunta aqui, mas também não gosto disso:
fonte
NOTA: Esta postagem acabou sendo muito mais detalhada e, portanto, fora do tópico, peço desculpas.
Dito isso, meus colegas lêem e acreditam que é valioso "em algum lugar". Este tópico não é o lugar. Eu gostaria de receber seus comentários sobre onde isso deve ir (eu sou novo no site).
De qualquer forma, esta é a versão C # em .NET 3.5, o que é incrível, pois funciona em qualquer tipo de coleção usando a semântica definida. Esta é uma medida padrão (reutilizar!) E não o desempenho ou a minimização do ciclo da CPU no cenário de desenvolvimento mais comum, embora isso nunca pareça ser o que acontece no mundo real (otimização prematura).
*** Método de extensão trabalhando em qualquer tipo de coleção e realizando uma ação delegada esperando um único valor do tipo, tudo executado em cada item ao contrário **
Requer 3.5:
Versões mais antigas do .NET ou você quer entender melhor a parte interna do Linq? Continue a ler .. Ou não ..
ASSUNÇÃO: No sistema de tipo .NET, o tipo Array herda da interface IEnumerable (não o IEnumerable genérico, apenas IEnumerable).
Isso é tudo que você precisa para iterar do início ao fim, no entanto, você deseja mover na direção oposta. Como IEnumerable funciona em Array do tipo 'objeto', qualquer tipo é válido,
MEDIDA CRÍTICA: Presumimos que se você pode processar qualquer sequência em ordem reversa que seja 'melhor' do que apenas ser capaz de fazê-lo em inteiros.
Solução a para .NET CLR 2.0-3.0:
Descrição: aceitaremos qualquer instância de implementação de IEnumerable com o mandato de que cada instância que ele contém seja do mesmo tipo. Portanto, se recebermos uma matriz, a matriz inteira contém instâncias do tipo X. Se qualquer outra instância for do tipo! = X, uma exceção é lançada:
Um serviço singleton:
public class ReverserService {private ReverserService () {}
[TestFixture] public class Testing123 {
fonte