Pour faire l'équivalent de la compréhension de liste Python, je fais ce qui suit:
some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}
Y a-t-il une meilleure façon de faire cela ... peut-être avec un seul appel de méthode?
ruby
list-comprehension
Lecture seulement
la source
la source
Réponses:
Si vous le souhaitez vraiment, vous pouvez créer une méthode Array # comprehend comme celle-ci:
Impressions:
Je le ferais probablement comme vous l'avez fait.
la source
[nil, nil, nil].comprehend {|x| x }
qui retourne[]
.compact!
renvoie nil au lieu du tableau lorsqu'aucun élément n'est modifié, donc je ne pense pas que cela fonctionne.Que diriez-vous:
Légèrement plus propre, du moins à mon goût, et selon un rapide test de référence environ 15% plus rapide que votre version ...
la source
some_array.map{|x| x * 3 unless x % 2}.compact
, qui est sans doute plus lisible / rubis-esque.unless x%2
n'a aucun effet puisque 0 est la vérité en ruby. Voir: gist.github.com/jfarmer/2647362J'ai fait une comparaison rapide des trois alternatives et la carte compacte semble vraiment être la meilleure option.
Test de performance (rails)
Résultats
la source
reduce
ce benchmark (voir stackoverflow.com/a/17703276 ).inject
==reduce
Il semble y avoir une certaine confusion parmi les programmeurs Ruby dans ce fil concernant ce qu'est la compréhension de liste. Chaque réponse suppose un tableau préexistant à transformer. Mais le pouvoir de la compréhension de liste réside dans un tableau créé à la volée avec la syntaxe suivante:
Ce qui suit serait un analogue de Ruby (la seule réponse adéquate dans ce fil, AFAIC):
Dans le cas ci-dessus, je crée un tableau d'entiers aléatoires, mais le bloc peut contenir n'importe quoi. Mais ce serait une compréhension de la liste Ruby.
la source
J'ai discuté de ce sujet avec Rein Henrichs, qui me dit que la solution la plus performante est
Cela a du bon sens car cela évite de créer des tableaux intermédiaires comme avec l'utilisation immuable de
Enumerable#inject
, et cela évite de développer le tableau, ce qui provoque une allocation. C'est aussi général que n'importe lequel des autres, sauf si votre collection peut contenir des éléments nuls.Je n'ai pas comparé ça avec
Il est possible que l'implémentation en C de Ruby
Enumerable#select
soit également très bonne.la source
Une solution alternative qui fonctionnera dans chaque implémentation et fonctionnera en temps O (n) au lieu de O (2n) est:
la source
2
chosesn
fois au lieu de1
chosen
fois, puis une autre1
chosen
fois :) Un avantage important deinject
/reduce
est qu'il conserve toutes lesnil
valeurs dans la séquence d'entrée qui est plus le comportement de liste comprehensionlyJe viens de publier le joyau de compréhension sur RubyGems, ce qui vous permet de faire ceci:
C'est écrit en C; le tableau n'est parcouru qu'une seule fois.
la source
Enumerable a une
grep
méthode dont le premier argument peut être un prédicat proc, et dont le deuxième argument facultatif est une fonction de mappage; donc ce qui suit fonctionne:Ce n'est pas aussi lisible que quelques autres suggestions (j'aime le joyau simple
select.map
ou compréhensible de l'histocrate d'anoiaque), mais ses points forts sont qu'il fait déjà partie de la bibliothèque standard, et qu'il est en un seul passage et n'implique pas la création de tableaux intermédiaires temporaires , et ne nécessite pas de valeur hors limites comme cellenil
utilisée dans lescompact
suggestions -using.la source
C'est plus concis:
la source
[1,2,3,4,5,6].select(&:even?).map(&3.method(:*))
Ça marche pour moi. C'est aussi propre. Oui, c'est la même chose que
map
, mais je pense que celacollect
rend le code plus compréhensible.semble vraiment mieux, après l'avoir vu ci-dessous.
la source
Comme Pedro l'a mentionné, vous pouvez fusionner les appels chaînés vers
Enumerable#select
etEnumerable#map
, en évitant une traversée des éléments sélectionnés. Cela est vrai car ilEnumerable#select
s'agit d'une spécialisation de pli ouinject
. J'ai posté une introduction hâtive au sujet dans le sous-répertoire Ruby.La fusion manuelle des transformations Array peut être fastidieuse, alors peut-être que quelqu'un pourrait jouer avec l'
comprehend
implémentation de Robert Gamble pour rendre ceselect
/map
pattern plus joli.la source
Quelque chose comme ça:
Appeler:
Qui renvoie:
la source
lazy
sur Array et ensuite:(1..6).lazy{|x|x*3 if x.even?}
Une autre solution mais peut-être pas la meilleure
ou
la source
Voici une façon d'aborder ceci:
Donc, fondamentalement, nous convertissons une chaîne en syntaxe ruby appropriée pour la boucle, puis nous pouvons utiliser la syntaxe python dans une chaîne à faire:
ou si vous n'aimez pas l'apparence de la chaîne ou si vous devez utiliser un lambda, nous pourrions renoncer à essayer de refléter la syntaxe python et faire quelque chose comme ceci:
la source
Ruby 2.7 introduit
filter_map
qui réalise à peu près ce que vous voulez (carte + compact):Vous pouvez en savoir plus ici .
la source
https://rubygems.org/gems/ruby_list_comprehension
plug sans vergogne pour mon joyau de compréhension de liste Ruby pour permettre la compréhension idiomatique de la liste Ruby
la source
Je pense que la plus grande compréhension de la liste serait la suivante:
Puisque Ruby nous permet de placer le conditionnel après l'expression, nous obtenons une syntaxe similaire à la version Python de la compréhension de liste. De plus, comme la
select
méthode n'inclut rien qui équivaut àfalse
, toutes les valeurs nulles sont supprimées de la liste résultante et aucun appel à compact n'est nécessaire comme ce serait le cas si nous avions utilisémap
ou à lacollect
place.la source