Tentei iterar para trás usando um intervalo e each
:
(4..0).each do |i|
puts i
end
==> 4..0
A iteração 0..4
escreve os números. Por outro Faixa r = 4..0
parece ser ok, r.first == 4
, r.last == 0
.
Parece-me estranho que a construção acima não produza o resultado esperado. Qual é a razão disso? Quais são as situações em que esse comportamento é razoável?
.each
instrução não modificou nada, não há "resultado" calculado para retornar. Quando este for o caso, Ruby normalmente retorna o objeto original em caso de sucesso enil
erro. Isso permite que você use expressões como esta como condições em umaif
instrução.Respostas:
Um intervalo é apenas isso: algo definido por seu início e fim, não por seu conteúdo. "Iterar" em um intervalo realmente não faz sentido em um caso geral. Considere, por exemplo, como você iria "iterar" no intervalo produzido por duas datas. Você iteraria por dia? por mês? por ano? por semana? Não está bem definido. IMO, o fato de que é permitido para intervalos futuros deve ser visto apenas como um método de conveniência.
Se quiser iterar para trás em um intervalo como esse, você sempre pode usar
downto
:Aqui estão mais alguns pensamentos de outros sobre por que é difícil permitir a iteração e lidar consistentemente com intervalos reversos.
fonte
.each
lá é redundante,5.downto(1) { |n| puts n }
funciona bem. Além disso, em vez de todas aquelas primeiras e últimas coisas, apenas faça(6..10).reverse_each
.= f.select :model_year, (Time.zone.now.year + 1).downto(Time.zone.now.year - 100).to_a
Que tal
(0..1).reverse_each
qual itera o intervalo para trás?fonte
(Date.today.beginning_of_year..Date.today.yesterday).reverse_each
ObrigadoIterando em um intervalo em Ruby com
each
chamadas osucc
método no primeiro objeto no intervalo.E 5 está fora do intervalo.
Você pode simular a iteração reversa com este hack:
John apontou que isso não funcionará se abranger 0. Isso:
Não posso dizer que gosto mesmo de qualquer um deles porque meio que obscurecem a intenção.
fonte
De acordo com o livro "Programming Ruby", o objeto Range armazena os dois pontos finais do intervalo e usa o
.succ
membro para gerar os valores intermediários. Dependendo do tipo de tipo de dados que você está usando em seu intervalo, você sempre pode criar uma subclasse deInteger
e redefinir o.succ
membro para que ele atue como um iterador reverso (você provavelmente também desejaria redefinir.next
).Você também pode obter os resultados que procura sem usar um intervalo. Experimente isto:
Isso irá variar de 4 a 0 em etapas de -1. No entanto, não sei se isso funcionará para qualquer coisa, exceto argumentos inteiros.
fonte
Outra maneira é
(1..10).to_a.reverse
fonte
Você pode até usar um
for
loop:que imprime:
fonte
se a lista não for tão grande. eu acho que
[*0..4].reverse.each { |i| puts i }
é a maneira mais simples.fonte
Como bta disse, o motivo é que
Range#each
enviasucc
para o seu início, depois para o resultado daquelasucc
chamada, e assim por diante até que o resultado seja maior que o valor final. Você não pode passar de 4 a 0 chamandosucc
e, na verdade, você já começa maior que o final.fonte
Eu adiciono uma outra possibilidade de como realizar a iteração no intervalo reverso. Eu não uso, mas é uma possibilidade. É um pouco arriscado remendar objetos de núcleo de rubi.
fonte
Isso funcionou para meu caso de uso preguiçoso
fonte
O OP escreveu
não 'Isso pode ser feito?' mas para responder à pergunta que não foi feita antes de chegar à pergunta que realmente foi feita:
Uma vez que é afirmado que reverse_each constrói um array inteiro, downto será claramente mais eficiente. O fato de que um designer de linguagem pode até mesmo considerar a implementação de coisas como essa meio que se vincula à resposta à pergunta real quando feita.
Para responder à pergunta como realmente feita ...
A razão é porque Ruby é uma linguagem infinitamente surpreendente. Algumas surpresas são agradáveis, mas há muitos comportamentos que estão completamente quebrados. Mesmo que alguns dos exemplos a seguir sejam corrigidos por versões mais recentes, há muitos outros e eles permanecem como acusações sobre a mentalidade do design original:
resulta em "" mas
resulta em
Você provavelmente esperaria que << e push fossem iguais para anexar a matrizes, mas
Você provavelmente esperaria que 'grep' se comportasse como seu equivalente de linha de comando do Unix, mas faz === não correspondendo a = ~, apesar de seu nome.
Vários métodos são inesperadamente apelidos uns para os outros, então você precisa aprender vários nomes para a mesma coisa - por exemplo,
find
edetect
- mesmo se você gosta da maioria dos desenvolvedores e usa apenas um ou outro. Praticamente o mesmo vale parasize
,count
elength
, exceto para classes que definem cada um de forma diferente, ou não definem um ou dois.A menos que alguém tenha implementado algo diferente - como o método principal
tap
foi redefinido em várias bibliotecas de automação para pressionar algo na tela. Boa sorte para descobrir o que está acontecendo, especialmente se algum módulo exigido por algum outro módulo manipulou outro módulo para fazer algo não documentado.O objeto de variável de ambiente, ENV não suporta 'mesclar', então você tem que escrever
Como um bônus, você pode até mesmo redefinir suas constantes ou as de outra pessoa se mudar de ideia sobre como elas deveriam ser.
fonte
grep
está de alguma forma, forma ou forma relacionada ao fato de que iterar em um intervalo vazio é um ambiente autônomo. Nem vejo como o fato de que a iteração em um intervalo vazio é um ambiente autônomo é de alguma forma ou forma "infinitamente surpreendente" e "totalmente quebrado".Para mim, a maneira mais simples é:
Outra maneira de iterar para enumeração:
fonte