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%2no 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
reduceen este punto de referencia (consulte stackoverflow.com/a/17703276 ).inject==reduceParece 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#injecty 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#selecttambié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
2cosasnveces en lugar de1cosasnveces y luego otra1cosanveces :) Una ventaja importante deinject/reducees que conserva cualquiernilvalor 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
grepmé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.mapo 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 senilusa en lascompactsugerencias -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 quecollecthace 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#selectyEnumerable#map, evitando un cruce sobre los elementos seleccionados. Esto es cierto porqueEnumerable#selectes 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
comprehendimplementación de Robert Gamble para hacer que esteselect/mappatrón sea más bonito.fuente
Algo como esto:
Llámalo:
Que devuelve:
fuente
lazyen 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_mapque 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
selectmé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 usadomapo en sucollectlugar.fuente