Para hacer el equivalente a las comprensiones de listas de Python, estoy haciendo lo siguiente:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
¿Hay una mejor manera de hacer esto ... quizás con una llamada a un método?
ruby
list-comprehension
Solo lectura
fuente
fuente
Respuestas:
Si realmente lo desea, puede crear un método de comprensión Array # como este:
Huellas dactilares:
Probablemente lo haría de la forma en que lo hizo.
fuente
[nil, nil, nil].comprehend {|x| x }
que regresa[]
.compact!
devuelve nil en lugar de la matriz cuando no se cambia ningún elemento, así que no creo que eso funcione.Que tal si:
Ligeramente más limpio, al menos para mi gusto, y según una prueba de referencia rápida, aproximadamente un 15% más rápido que su versión ...
fuente
some_array.map{|x| x * 3 unless x % 2}.compact
, que posiblemente sea más legible / ruby-esque.unless x%2
no tiene ningún efecto ya que 0 es verdadero en ruby. Ver: gist.github.com/jfarmer/2647362Hice un benchmark rápido comparando las tres alternativas y map-compact realmente parece ser la mejor opción.
Prueba de rendimiento (rieles)
Resultados
fuente
reduce
en este punto de referencia (consulte stackoverflow.com/a/17703276 ).inject
==reduce
Parece haber cierta confusión entre los programadores de Ruby en este hilo sobre qué es la comprensión de listas. Cada respuesta asume alguna matriz preexistente para transformar. Pero el poder de la comprensión de listas radica en una matriz creada sobre la marcha con la siguiente sintaxis:
Lo siguiente sería un análogo en Ruby (la única respuesta adecuada en este hilo, AFAIC):
En el caso anterior, estoy creando una matriz de enteros aleatorios, pero el bloque podría contener cualquier cosa. Pero esta sería una lista de comprensión de Ruby.
fuente
Discutí este tema con Rein Henrichs, quien me dice que la solución de mejor rendimiento es
Esto tiene sentido porque evita la construcción de matrices intermedias como ocurre con el uso inmutable de
Enumerable#inject
y evita el crecimiento de la matriz, lo que provoca la asignación. Es tan general como cualquiera de los demás, a menos que su colección pueda contener elementos nulos.No he comparado esto con
Es posible que la implementación de Ruby en C
Enumerable#select
también sea muy buena.fuente
Una solución alternativa que funcionará en todas las implementaciones y se ejecutará en tiempo O (n) en lugar de O (2n) es:
fuente
2
cosasn
veces en lugar de1
cosasn
veces y luego otra1
cosan
veces :) Una ventaja importante deinject
/reduce
es que conserva cualquiernil
valor en la secuencia de entrada, que es un comportamiento más comprensivo de listasAcabo de publicar la gema de comprensión en RubyGems, que te permite hacer esto:
Está escrito en C; la matriz solo se atraviesa una vez.
fuente
Enumerable tiene un
grep
método cuyo primer argumento puede ser un predicado proc, y cuyo segundo argumento opcional es una función de mapeo; entonces lo siguiente funciona:Esto no es tan legible como un par de otras sugerencias (me gusta la gema de comprensión simple
select.map
o histocrat de anoiaque), pero sus puntos fuertes son que ya es parte de la biblioteca estándar, es de un solo paso y no implica la creación de matrices intermedias temporales. , y no requiere un valor fuera de límites como el que senil
usa en lascompact
sugerencias -using.fuente
Esto es más conciso:
fuente
[1,2,3,4,5,6].select(&:even?).map(&3.method(:*))
Funciona para mi. También está limpio. Sí, es lo mismo que
map
, pero creo quecollect
hace que el código sea más comprensible.en realidad se ve mejor, después de verlo a continuación.
fuente
Como mencionó Pedro, puede fusionar las llamadas encadenadas a
Enumerable#select
yEnumerable#map
, evitando un cruce sobre los elementos seleccionados. Esto es cierto porqueEnumerable#select
es una especialización de fold oinject
. Publiqué una introducción apresurada al tema en el subreddit de Ruby.Fusionar manualmente las transformaciones de Array puede ser tedioso, por lo que tal vez alguien pueda jugar con la
comprehend
implementación de Robert Gamble para hacer que esteselect
/map
patrón sea más bonito.fuente
Algo como esto:
Llámalo:
Que devuelve:
fuente
lazy
en Array y luego?(1..6).lazy{|x|x*3 if x.even?}
Otra solución pero quizás no la mejor
o
fuente
Esta es una forma de abordar esto:
así que básicamente estamos convirtiendo una cadena a la sintaxis ruby adecuada para el bucle, luego podemos usar la sintaxis de Python en una cadena para hacer:
o si no le gusta el aspecto de la cadena o tener que usar una lambda, podríamos renunciar al intento de reflejar la sintaxis de Python y hacer algo como esto:
fuente
Se introdujo Ruby 2.7,
filter_map
que prácticamente logra lo que desea (mapa + compacto):Puedes leer más sobre esto aquí .
fuente
https://rubygems.org/gems/ruby_list_comprehension
enchufe desvergonzado para mi gema de comprensión de la lista Ruby para permitir comprensiones idiomáticas de la lista Ruby
fuente
Creo que la más lista de comprensión sería la siguiente:
Dado que Ruby nos permite colocar el condicional después de la expresión, obtenemos una sintaxis similar a la versión Python de la lista de comprensión. Además, dado que el
select
método no incluye nada que equivalga afalse
, todos los valores nulos se eliminan de la lista resultante y no es necesaria ninguna llamada a compact, como sería el caso si hubiéramos usadomap
o en sucollect
lugar.fuente