402 Stimmen

Was ist der "richtige" Weg, um durch ein Array in Ruby zu iterieren?

PHP ist in dieser Hinsicht trotz all seiner Schwächen ziemlich gut. Es gibt keinen Unterschied zwischen einem Array und einem Hash (vielleicht bin ich naiv, aber das scheint mir offensichtlich richtig zu sein), und um durch beide zu iterieren, machen Sie einfach

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

In Ruby gibt es eine Reihe von Möglichkeiten, diese Art von Dingen zu tun:

array.length.times do |i|
end

array.each

array.each_index

for i in array

Hashes machen mehr Sinn, da ich einfach immer

hash.each do |key, value|

Warum kann ich dies nicht für Arrays tun? Wenn ich mir nur eine Methode merken möchte, kann ich wohl Folgendes verwenden each_index (da es sowohl den Index als auch den Wert verfügbar macht), aber es ist ärgerlich, wenn man die array[index] statt nur value .


Ach ja, ich vergaß array.each_with_index . Dieser hier ist jedoch scheiße, denn er geht |value, key| y hash.each geht |key, value| ! Ist das nicht wahnsinnig?

639voto

Robert Gamble Punkte 101657

Damit werden alle Elemente durchlaufen:

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

# Output:

1
2
3
4
5
6

Dadurch werden alle Elemente durchlaufen und Sie erhalten den Wert und den Index:

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

# Output:

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

Bei Ihrer Frage bin ich mir nicht ganz sicher, wonach Sie suchen.

117voto

AShelly Punkte 33678

Ich glaube, es gibt niemanden rechts Weise. Es gibt viele verschiedene Möglichkeiten der Iteration, und jede hat ihre eigene Nische.

  • each ist für viele Verwendungszwecke ausreichend, da ich mich oft nicht um die Indizes kümmere.
  • each_ with _index funktioniert wie Hash#each - Sie erhalten den Wert und den Index.
  • each_index - nur die Indizes. Ich verwende diese Option nicht oft. Äquivalent zu "Länge.mal".
  • map ist eine weitere Möglichkeit der Iteration, die nützlich ist, wenn Sie ein Array in ein anderes umwandeln wollen.
  • select ist der Iterator, der zu verwenden ist, wenn Sie eine Teilmenge auswählen möchten.
  • inject ist nützlich, um Summen oder Produkte zu erzeugen oder ein einzelnes Ergebnis zu sammeln.

Es mag Ihnen viel erscheinen, aber keine Sorge, Sie kommen auch ohne diese Informationen aus. Aber wenn du anfängst, die verschiedenen Methoden zu lernen und zu verwenden, wird dein Code sauberer und klarer, und du bist auf dem besten Weg, Ruby zu beherrschen.

64voto

Ich will damit nicht sagen, dass Array -> |value,index| y Hash -> |key,value| ist nicht verrückt (siehe den Kommentar von Horace Loeb), aber ich sage, dass es einen vernünftigen Weg gibt, diese Vereinbarung zu erwarten.

Wenn ich mit Arrays zu tun habe, konzentriere ich mich auf die Elemente im Array (nicht auf den Index, da dieser vergänglich ist). Die Methode ist each mit index, d.h. each+index, oder |each,index|, oder |value,index| . Dies ist auch damit vereinbar, dass der Index als optionales Argument betrachtet wird, z. B. ist |value| äquivalent zu |value,index=nil|, was mit |value,index| vereinbar ist.

Wenn ich mit Hashes zu tun habe, konzentriere ich mich oft mehr auf die Schlüssel als auf die Werte, und ich befasse mich normalerweise mit Schlüsseln und Werten in dieser Reihenfolge, entweder key => value o hash[key] = value .

Wenn Sie Duck-Typing wollen, dann verwenden Sie entweder explizit eine definierte Methode, wie Brent Longborough gezeigt hat, oder eine implizite Methode, wie maxhawkins gezeigt hat.

Bei Ruby geht es darum, die Sprache an den Programmierer anzupassen, nicht darum, dass der Programmierer sich an die Sprache anpasst. Deshalb gibt es auch so viele Möglichkeiten. Es gibt so viele Möglichkeiten, über etwas nachzudenken. In Ruby wählt man die naheliegendste, und der Rest des Codes ergibt sich in der Regel sehr sauber und präzise.

Was die ursprüngliche Frage, "Was ist der "richtige" Weg, um durch ein Array in Ruby iterieren?", nun, ich denke, der Kern Weg (dh ohne leistungsfähige syntaktische Zucker oder objektorientierte Macht) ist zu tun:

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

Aber in Ruby geht es um mächtigen syntaktischen Zucker und objektorientierte Macht, aber hier ist das Äquivalent für Hashes, und die Schlüssel können geordnet sein oder nicht:

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

Meine Antwort lautet also: "Der "richtige" Weg, um in Ruby durch ein Array zu iterieren, hängt von Ihnen (d.h. dem Programmierer oder dem Programmierteam) und dem Projekt ab". Der bessere Ruby-Programmierer trifft die bessere Wahl (welche syntaktische Kraft und/oder welcher objektorientierte Ansatz). Der bessere Ruby-Programmierer sucht weiter nach mehr Möglichkeiten.


Jetzt möchte ich eine andere Frage stellen: "Was ist der "richtige" Weg, um in Ruby rückwärts durch einen Bereich zu iterieren?"! (Diese Frage hat mich auf diese Seite geführt.)

Das ist eine schöne Sache (für die Stürmer):

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

aber ich tue es nicht gerne (für den Rückwärtsgang):

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

Ich habe eigentlich nichts dagegen, das zu tun, aber wenn ich unterrichte, rückwärts zu gehen, möchte ich meinen Schülern eine schöne Symmetrie zeigen (d.h. mit minimalen Unterschieden, z.B. nur eine Umkehrung hinzufügen, oder einen Schritt -1, aber ohne irgendetwas anderes zu ändern). Das können Sie tun (für die Symmetrie):

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

y

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

was ich nicht sehr mag, aber man kann nicht

(*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

Sie könnten schließlich Folgendes tun

class Range

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

end

aber ich möchte eher reines Ruby unterrichten als objektorientierte Ansätze (noch). Ich möchte rückwärts iterieren:

  • ohne ein Array zu erstellen (z.B. 0..1000000000)
  • funktioniert für jeden Bereich (z. B. Strings, nicht nur Integer)
  • ohne zusätzliche objektorientierte Leistung (d.h. keine Klassenänderung)

Ich glaube, das ist unmöglich, ohne eine pred Methode, d.h. die Klasse Range muss für die Verwendung dieser Methode geändert werden. Wenn Sie dies tun können, lassen Sie mich bitte wissen, andernfalls Bestätigung der Unmöglichkeit würde geschätzt werden, obwohl es enttäuschend wäre. Vielleicht Ruby 1.9 Adressen dies.

(Danke, dass Sie sich die Zeit genommen haben, dies zu lesen.)

20voto

J Cooper Punkte 16641

Verwenden Sie each_with_index, wenn Sie beides benötigen.

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

12voto

Pistos Punkte 22071

Die anderen Antworten sind in Ordnung, aber ich wollte noch auf eine andere periphere Sache hinweisen: Arrays sind geordnet, während Hashes in 1.8 nicht geordnet sind. (In Ruby 1.9 sind Hashes nach der Einfügereihenfolge der Schlüssel geordnet.) Es würde also vor 1.9 keinen Sinn machen, über einen Hash auf die gleiche Weise/Sequenz zu iterieren wie über Arrays, die schon immer eine eindeutige Ordnung hatten. Ich weiß nicht, was die Standardreihenfolge für assoziative PHP-Arrays ist (anscheinend ist mein Google-Fu auch nicht stark genug, um das herauszufinden), aber ich weiß nicht, wie man reguläre PHP-Arrays und assoziative PHP-Arrays in diesem Zusammenhang als "gleich" betrachten kann, da die Reihenfolge für assoziative Arrays undefiniert scheint.

Daher erscheint mir der Ruby-Weg klarer und intuitiver :)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X