365 votes

Quelle est la "bonne" façon d'itérer dans un tableau en Ruby ?

PHP, malgré toutes ses faiblesses, est plutôt bon dans ce domaine. Il n'y a pas de différence entre un tableau et un hachage (je suis peut-être naïf, mais cela me semble évident), et pour itérer dans l'un ou l'autre, il suffit de faire

foreach (array/hash as $key => $value)

En Ruby, il y a plusieurs façons de faire ce genre de choses :

array.length.times do |i|
end

array.each

array.each_index

for i in array

Les hachages ont plus de sens, puisque j'utilise toujours

hash.each do |key, value|

Pourquoi ne puis-je pas faire cela pour les tableaux ? Si je veux me souvenir d'une seule méthode, je suppose que je peux utiliser each_index (puisqu'il rend l'indice et la valeur disponibles), mais il est ennuyeux de devoir faire array[index] au lieu de simplement value .


Ah oui, j'ai oublié array.each_with_index . Cependant, celui-ci est nul parce qu'il va |value, key| et hash.each va sur |key, value| ! N'est-ce pas de la folie ?

589voto

Robert Gamble Points 41984

Ceci va itérer à travers tous les éléments :

array = [1, 2, 3, 4, 5, 6]
array.each { |x| puts x }

Imprimés :

1
2
3
4
5
6

Cela va itérer à travers tous les éléments en vous donnant la valeur et l'index :

array = ["A", "B", "C"]
array.each_with_index {|val, index| puts "#{val} => #{index}" }

Imprimés :

A => 0
B => 1
C => 2

D'après votre question, je ne sais pas exactement lequel vous recherchez.

98voto

AShelly Points 17389

Je pense qu'il n'y a personne droite manière. Il existe de nombreuses façons différentes d'itérer, et chacune a sa propre niche.

  • each est suffisant pour de nombreux usages, puisque je ne me soucie pas souvent des index.
  • each_ with _index agit comme Hash#each - vous obtenez la valeur et l'index.
  • each_index - Je ne l'utilise pas souvent, mais je suis sûr qu'elle a sa place.
  • map est une autre façon d'itérer, utile lorsque vous voulez transformer un tableau en un autre.
  • select est l'itérateur à utiliser lorsque vous voulez choisir un sous-ensemble.
  • inject est utile pour générer des sommes ou des produits.

Cela peut sembler beaucoup à retenir, mais ne vous inquiétez pas, vous pouvez vous en sortir sans les connaître tous. Mais au fur et à mesure que vous apprendrez et utiliserez les différentes méthodes, votre code deviendra plus propre et plus clair, et vous serez sur la voie de la maîtrise de Ruby.

60voto

Je ne dis pas que Array -> |value,index| et Hash -> |key,value| n'est pas insensé (voir le commentaire d'Horace Loeb), mais je dis qu'il y a une façon saine d'envisager cet arrangement.

Lorsque je traite des tableaux, je me concentre sur les éléments du tableau (et non sur l'indice, car celui-ci est transitoire). La méthode est each with index, c'est-à-dire each+index, ou |each,index|, ou encore |value,index| . Ceci est également cohérent avec le fait que l'indice est considéré comme un argument optionnel, par exemple, |valeur| est équivalent à |valeur,indice=nil| qui est cohérent avec |valeur,indice|.

Lorsque je traite des hachages, je me concentre souvent davantage sur les clés que sur les valeurs, et je traite généralement les clés et les valeurs dans cet ordre, soit key => value ou hash[key] = value .

Si vous voulez un typage en canard, alors utilisez soit explicitement une méthode définie comme l'a montré Brent Longborough, soit une méthode implicite comme l'a montré maxhawkins.

Ruby consiste à adapter le langage au programmeur, et non à adapter le programmeur au langage. C'est pourquoi il y a tant de façons de faire. Il y a tellement de façons de penser à quelque chose. En Ruby, vous choisissez celle qui est la plus proche et le reste du code tombe généralement de manière extrêmement soignée et concise.

Pour ce qui est de la question initiale, "Quelle est la "bonne" façon d'itérer dans un tableau en Ruby ?", eh bien, je pense que la façon de base (c'est-à-dire sans sucre syntaxique puissant ou puissance orientée objet) est de faire :

for index in 0 ... array.size
  puts "array[#{index}] = #{array[index].inspect}"
end

Mais Ruby n'est qu'un puissant sucre syntaxique et une puissance orientée objet, mais quoi qu'il en soit voici l'équivalent pour les hachages, et les clés peuvent être ordonnées ou non :

for key in hash.keys.sort
  puts "hash[#{key.inspect}] = #{hash[key].inspect}"
end

Ma réponse est donc : "La "bonne" façon d'itérer dans un tableau en Ruby dépend de vous (c'est-à-dire du programmeur ou de l'équipe de programmation) et du projet". Le meilleur programmeur Ruby fait le meilleur choix (de quelle puissance syntaxique et/ou de quelle approche orientée objet). Le meilleur programmeur Ruby continue à chercher d'autres moyens.


Maintenant, je veux poser une autre question : " Quelle est la " bonne " façon d'itérer dans une plage en Ruby à l'envers ? " ! (C'est grâce à cette question que je suis arrivé sur cette page).

C'est agréable à faire (pour les attaquants) :

(1..10).each{|i| puts "i=#{i}" }

mais je n'aime pas le faire (pour les arrières) :

(1..10).to_a.reverse.each{|i| puts "i=#{i}" }

En fait, ça ne me dérange pas trop de faire ça, mais quand j'enseigne le retour en arrière, je veux montrer à mes élèves une belle symétrie (c'est-à-dire avec une différence minimale, par exemple en ajoutant seulement un retour en arrière, ou un pas -1, mais sans modifier quoi que ce soit d'autre). Vous pouvez faire (pour la symétrie) :

(a=*1..10).each{|i| puts "i=#{i}" }

et

(a=*1..10).reverse.each{|i| puts "i=#{i}" }

que je n'aime pas beaucoup, mais tu ne peux pas faire

(*1..10).each{|i| puts "i=#{i}" }
(*1..10).reverse.each{|i| puts "i=#{i}" }
#
(1..10).step(1){|i| puts "i=#{i}" }
(1..10).step(-1){|i| puts "i=#{i}" }
#
(1..10).each{|i| puts "i=#{i}" }
(10..1).each{|i| puts "i=#{i}" }   # I don't want this though.  It's dangerous

Vous pourriez finalement faire

class Range

  def each_reverse(&block)
    self.to_a.reverse.each(&block)
  end

end

mais je veux enseigner le Ruby pur plutôt que les approches orientées objet (pour l'instant). Je voudrais itérer à l'envers :

  • sans créer un tableau (considérer 0..1000000000)
  • fonctionne pour n'importe quelle plage (par exemple, les chaînes de caractères, pas seulement les nombres entiers)
  • sans utiliser de puissance orientée objet supplémentaire (c'est-à-dire sans modification de classe)

Je pense que c'est impossible sans définir un pred ce qui implique de modifier la classe Range pour l'utiliser. Si vous pouvez le faire, veuillez me le faire savoir, sinon une confirmation d'impossibilité serait appréciée bien que ce soit décevant. Peut-être que Ruby 1.9 résout ce problème.

(Merci de votre temps pour lire ceci).

20voto

J Cooper Points 7695

Utilisez each_with_index lorsque vous avez besoin des deux.

ary.each_with_index { |val, idx| # ...

12voto

Pistos Points 8997

Les autres réponses sont très bien, mais je voulais signaler une autre chose périphérique : les tableaux sont ordonnés, alors que les hachages ne le sont pas dans la version 1.8. (Dans Ruby 1.9, les hachages sont ordonnés par l'ordre d'insertion des clés). Ainsi, avant la version 1.9, cela n'aurait pas de sens d'itérer sur un hachage de la même manière que sur des tableaux, qui ont toujours eu un ordre défini. Je ne sais pas quel est l'ordre par défaut pour les tableaux associatifs de PHP (apparemment mon google fu n'est pas assez fort pour le découvrir, non plus), mais je ne sais pas comment vous pouvez considérer les tableaux réguliers de PHP et les tableaux associatifs de PHP comme étant "les mêmes" dans ce contexte, puisque l'ordre pour les tableaux associatifs semble indéfini.

En tant que telle, la méthode Ruby me semble plus claire et intuitive :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X