Die Programmiersprache Ruby (German Edition)
beispielsweise die Zeichen eines String einem Array von Zeichen zuzuordnen, verwenden Sie einfach
.chars.map
:
"hello".chars.map {|c| c.succ } # => ["i", "f", "m", "m", "p"]
Hier sehen Sie einige weitere Beispiele, die darauf beruhen, dass Iteratormethoden Enumerator-Objekte zurückgeben. Beachten Sie, dass nicht nur die von
Enumerable
definierten Iteratormethoden Enumerator-Objekte zurückgeben können; numerische Iteratoren wie
times
und
upto
tun dasselbe:
enumerator = 3.times # Ein Enumerator-Objekt
enumerator.each {|x| print x } # Gibt "012" aus
# downto liefert mit select-Methode Enumerator zurück
10.downto(1).select {|x| x%2==0} # => [10,8,6,4,2]
# Iterator each_byte liefert mit to_a Enumerator zurück
"hallo".each_byte.to_a # => [104, 97, 108, 108, 111]
Sie können dieses Verhalten in Ihren eigenen Iteratormethoden nachahmen, indem Sie
self.to_enum
zurückgeben, wenn kein Block übergeben wird. Hier beispielsweise eine Version des weiter oben gezeigten Iterators
twice
, die einen Enumerator zurückgeben kann, wenn kein Block bereitgestellt wird:
def twice
if block_given?
yield
yield
else
self.to_enum(:twice)
end
end
In Ruby 1.9 definieren Enumerator-Objekte eine
with_index
-Methode, die im Enumerator-Modul von Ruby 1.8 nicht zur Verfügung steht.
with_index
liefert einfach einen neuen Enumerator zurück, der einen Indexparameter zu der Iteration hinzufügt. Der folgende Code liefert beispielsweise einen neuen Enumerator zurück, der die Zeichen eines String und ihren Index innerhalb des String weitergibt:
enumerator = s.each_char.with_index
Behalten Sie schließlich im Kopf, dass Enumeratoren sowohl in Ruby 1.8 als auch in Ruby 1.9
Enumerable
-Objekte sind, die mit der
for
-Schleife verwendet werden können, zum Beispiel:
for line, number in text.each_line.with_index
print "#{number+1}: #{line}"
end
5.3.5 Externe Iteratoren
Unsere Diskussion über Enumeratoren hat sich bisher auf ihre Verwendung als
Enumerable
-Proxy-Objekte konzentriert. In Ruby 1.9 haben Enumeratoren allerdings noch einen weiteren wichtigen Verwendungszweck: Sie sind externe Iteratoren. Sie können einen Enumerator verwenden, um über die Elemente einer Sammlung zu iterieren, indem Sie wiederholt die Methode
next
aufrufen. Wenn es keine Elemente mehr gibt, löst diese Methode eine
StopIteration
-Ausnahme aus:
iterator = 9.downto(1) # Enumerator als externer Iterator
begin # Deshalb können wir unten rescue verwenden
print iterator.next while true # Wiederholt Methode next aufrufen
rescue StopIteration # Wenn keine Werte mehr da sind
puts "... Abflug!" # Erwartete Nicht-Ausnahmebedingung
end
Externe Iteratoren sind recht einfach zu benutzen: Rufen Sie einfach jedes Mal, wenn Sie ein weiteres Element haben wollen,
next
auf. Wenn keine Elemente mehr übrig sind, löst
next
eine
StopIteration
-Ausnahme aus. Das mag ungewöhnlich erscheinen – eine Ausnahme wird für eine erwartete Abbruchbedingung statt für ein unerwartetes Ausnahmeereignis ausgelöst. (
StopIteration
stammt von
StandardError
und
IndexError
ab; beachten Sie, dass es eine der wenigen Ausnahmeklassen ist, die nicht das Wort »Error« im Namen haben.) Ruby folgt mit diesem externen Iterationsverfahren dem Beispiel von Python. Die Behandlung des Schleifenabbruchs als Ausnahme macht die Schleifenlogik extrem einfach; es besteht kein Bedarf, den Rückgabewert von
next
auf einen speziellen Iterationsendwert zu überprüfen, und es besteht auch kein Bedarf, vor dem Aufruf von
next
irgendeine Art von
next?
-Prädikat aufzurufen.
----
Interne und externe Iteratoren im Vergleich
Die »Gang of Four« definiert in ihrem Buch über Design Patterns interne und externe Iteratoren und stellt sie klar einander gegenüber: [ 23 ]
Eine grundlegende Frage ist, welcher Beteiligte die Iteration steuert, der Iterator oder der Client, der den Iterator verwendet. Wenn der Client die Iteration steuert, wird der Iterator als externer Iterator bezeichnet, und wenn der Iterator sie steuert, dann ist er ein interner Iterator . Clients, die einen externen Iterator verwenden, müssen die Iteration vorantreiben und explizit das nächste Element vom Iterator anfordern. Im Gegensatz dazu übergibt ein Client einem internen Iterator eine durchzuführende Operation, und der Iterator wendet diese Operation auf jedes Element an ...
Externe Iteratoren sind flexibler als interne. Es ist beispielsweise einfach, zwei Sammlungen mithilfe eines externen Iterators auf Gleichheit zu prüfen, aber mit
Weitere Kostenlose Bücher