Para fazer o equivalente às compreensões de lista Python, estou fazendo o seguinte:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
Existe uma maneira melhor de fazer isso ... talvez com uma chamada de método?
ruby
list-comprehension
Somente leitura
fonte
fonte
Respostas:
Se você realmente quiser, pode criar um método Array # compreender como este:
Impressões:
Eu provavelmente faria do jeito que você fez.
fonte
[nil, nil, nil].comprehend {|x| x }
que retorna[]
.compact!
retorna nil em vez da matriz quando nenhum item é alterado, então não acho que funcione.Que tal:
Um pouco mais limpo, pelo menos para o meu gosto, e de acordo com um teste rápido de benchmark cerca de 15% mais rápido que a sua versão ...
fonte
some_array.map{|x| x * 3 unless x % 2}.compact
, que é indiscutivelmente mais legível / ruby-esque.unless x%2
não tem efeito porque 0 é verdadeiro em rubi. Consulte: gist.github.com/jfarmer/2647362Fiz um rápido benchmark comparando as três alternativas e o map-compact realmente parece ser a melhor opção.
Teste de desempenho (Rails)
Resultados
fonte
reduce
neste benchmark também (consulte stackoverflow.com/a/17703276 ).inject
==reduce
Parece haver alguma confusão entre os programadores Ruby neste tópico sobre o que é compreensão de lista. Cada resposta assume alguma matriz preexistente para transformar. Mas o poder da compreensão de lista está em uma matriz criada instantaneamente com a seguinte sintaxe:
O seguinte seria um análogo em Ruby (a única resposta adequada neste tópico, AFAIC):
No caso acima, estou criando uma matriz de inteiros aleatórios, mas o bloco pode conter qualquer coisa. Mas isso seria uma compreensão de lista Ruby.
fonte
Eu discuti este tópico com Rein Henrichs, que me disse que a solução de melhor desempenho é
Isso faz sentido porque evita a construção de Arrays intermediários, como ocorre com o uso imutável de
Enumerable#inject
, e evita o crescimento do Array, o que causa a alocação. É tão geral quanto qualquer um dos outros, a menos que sua coleção possa conter elementos nulos.Eu não comparei isso com
É possível que a implementação em C do Ruby
Enumerable#select
também seja muito boa.fonte
Uma solução alternativa que funcionará em todas as implementações e rodará em tempo O (n) em vez de O (2n) é:
fonte
2
coisasn
vezes em vez de1
coisasn
vezes e depois outra1
coisan
vezes :) Uma vantagem importante deinject
/reduce
é que ele preserva quaisquernil
valores na sequência de entrada, que é um comportamento mais abrangente de listaAcabei de publicar a gem de compreensão para RubyGems, que permite que você faça isso:
Está escrito em C; a matriz é percorrida apenas uma vez.
fonte
Enumerable tem um
grep
método cujo primeiro argumento pode ser um predicado proc e cujo segundo argumento opcional é uma função de mapeamento; então o seguinte funciona:Isso não é tão legível quanto algumas outras sugestões (eu gosto da gema simples de
select.map
anoiaque ou de compreensão de histocrata), mas seus pontos fortes são que ela já faz parte da biblioteca padrão, é de passagem única e não envolve a criação de matrizes intermediárias temporárias , e não requer um valor fora dos limites, comonil
usado nascompact
sugestões -using.fonte
Isso é mais conciso:
fonte
[1,2,3,4,5,6].select(&:even?).map(&3.method(:*))
Isso funciona para mim. Também está limpo. Sim, é o mesmo que
map
, mas acho quecollect
torna o código mais compreensível.na verdade, parece melhor, depois de ver abaixo.
fonte
Como o Pedro mencionou, você pode fundir as chamadas encadeadas para
Enumerable#select
eEnumerable#map
, evitando uma passagem sobre os elementos selecionados. Isso é verdade porqueEnumerable#select
é uma especialização de dobra ouinject
. Eu postei uma introdução apressada ao tópico no subreddit Ruby.A fusão manual das transformações de Array pode ser entediante, então talvez alguém possa brincar com a
comprehend
implementação de Robert Gamble para tornar esteselect
/map
padrão mais bonito.fonte
Algo assim:
Chame-o:
Que retorna:
fonte
lazy
em Array e então:(1..6).lazy{|x|x*3 if x.even?}
Outra solução, mas talvez não a melhor
ou
fonte
Esta é uma maneira de abordar isso:
então, basicamente, estamos convertendo uma string para a sintaxe ruby apropriada para loop, então podemos usar a sintaxe python em uma string para fazer:
ou se você não gostar da aparência da string ou de ter que usar um lambda, poderíamos abandonar a tentativa de espelhar a sintaxe python e fazer algo assim:
fonte
Ruby 2.7 introduzido,
filter_map
que praticamente atinge o que você deseja (mapa + compacto):Você pode ler mais sobre isso aqui .
fonte
https://rubygems.org/gems/ruby_list_comprehension
plugue descarado para minha gem de Compreensão de Lista Ruby para permitir compreensões idiomáticas de lista Ruby
fonte
Acho que a compreensão mais abrangente de lista seria a seguinte:
Como o Ruby nos permite colocar o condicional após a expressão, obtemos uma sintaxe semelhante à versão Python da compreensão de lista. Além disso, como o
select
método não inclui nada que seja igual afalse
, todos os valores nulos são removidos da lista resultante e nenhuma chamada para compactar é necessária como seria o caso se tivéssemos usadomap
ou emcollect
vez disso.fonte