Die Programmiersprache Ruby (German Edition)
diese
FibonacciGenerator
-Klasse
Enumerable
machen können, indem wir das Modul
Enumerable
einfügen und die folgende
each
-Methode hinzufügen (die wir zuerst in „5.3.5 Externe Iteratoren“ verwendet haben):
def each
loop { yield self.next }
end
Nehmen wir umgekehrt an, wir haben ein
Enumerable
-Objekt und möchten einen Generator im
Enumerator
-Stil daraus machen. Wir können diese Klasse verwenden:
class Generator
def initialize(enumerable)
@enumerable = enumerable # Enumerable-Objekt merken
create_fiber # Fiber zur Enumeration erzeugen
end
def next # Nächstes Element zurückgeben
@fiber.resume # durch resume der Fiber
end
def rewind # Enumeration neu starten
create_fiber # durch Erzeugen einer neuen Fiber
end
private
def create_fiber # Fiber für die Enumeration erzeugen
@fiber = Fiber.new do # Eine neue Fiber erzeugen
@enumerable.each do |x| # Die Methode each verwenden
Fiber.yield(x) # Aber Pause, um Werte zurückzugeben
end
raise StopIteration # Ausnahme, wenn keine Werte mehr da sind
end
end
end
g = Generator.new(1..10) # So wird aus einem Enumerable ein Generator
loop { print g.next } # Und so wird er wie ein Enumerator verwendet
g.rewind # So wird er neu gestartet
g = (1..10).to_enum # Die Methode to_enum tut dasselbe
loop { print g.next }
Es ist zwar lehrreich, die Implementierung dieser
Generator
-Klasse zu studieren, aber die Klasse selbst stellt gegenüber der Methode
to_enum
keine zusätzliche Funktionalität bereit.
5.8.2.3 Fortgeschrittene Fiber-Features
Das Modul
fiber
in der Standardbibliothek aktiviert zusätzliche, noch leistungsfähigere Features der Fiber. Um diese Features zu nutzen, müssen Sie schreiben:
require 'fiber'
Allerdings sollten Sie diese zusätzlichen Features vermeiden, wann immer möglich, denn
sie werden nicht von allen Implementierungen unterstützt. JRuby kann sie auf aktuellen Java-VMs beispielsweise nicht unterstützen.
sie sind so mächtig, dass ihre fehlerhafte Verwendung die Ruby-VM zum Absturz bringen kann.
Die Kern-Features der Klasse
Fiber
implementieren Semicoroutinen. Sie sind keine echten Coroutinen, weil es eine grundlegende Asymmetrie zwischen der aufrufenden Stelle und der Fiber gibt: Die aufrufende Stelle verwendet
resume
und die Fiber
yield
. Wenn Sie die Bibliothek
fiber
importieren, erhält die Klasse
Fiber
jedoch eine Methode namens
transfer
, die jeder Fiber erlaubt, die Kontrolle an jede andere Fiber zu übergeben. Hier sehen Sie ein Beispiel, in dem zwei Fiber die Methode
transfer
verwenden, um die Kontrolle (und die Werte) hin- und herzureichen:
require 'fiber'
f = g = nil
f = Fiber.new {|x| # 1:
puts "f1: #{x}" # 2: Ausgabe "f1: 1"
x = g.transfer(x+1) # 3: Übergabe 2 an Zeile 8
puts "f2: #{x}" # 4: Ausgabe "f2: 3"
x = g.transfer(x+1) # 5: Übergabe 4 an Zeile 10
puts "f3: #{x}" # 6: Ausgabe "f3: 5"
x + 1 # 7: Rückgabe 6 an Zeile 13
}
g = Fiber.new {|x| # 8:
puts "g1: #{x}" # 9: Ausgabe "g1: 2"
x = f.transfer(x+1) #10: Übergabe 3 an Zeile 3
puts "g2: #{x}" #11: Ausgabe "g2: 4"
x = f.transfer(x+1) #12: Rückgabe 5 an Zeile 5
}
puts f.transfer(1) #13: Übergabe 1 an Zeile 1
Dieser Code erzeugt die folgende Ausgabe:
f1: 1
g1: 2
f2: 3
g2: 4
f3: 5
6
Sie werden diese
transfer
-Methode wahrscheinlich niemals benutzen müssen, aber ihre Existenz hilft, den Namen »Fiber« zu erläutern. Fiber können als unabhängige Ausführungspfade innerhalb eines einzelnen Ausführungs-Threads betrachtet werden. Anders als bei Threads gibt es jedoch keinen Scheduler, der die Kontrolle an die Fiber verteilt; Fiber müssen sich die Zeit explizit selbst mit
transfer
einteilen.
Zusätzlich zur Methode
transfer
definiert die Bibliothek
fiber
noch eine Instanzmethode namens
alive?
, um herauszufinden, ob der Rumpf einer Fiber noch läuft, und die Klassenmethode
current
, um das Fiber-Objekt zurückzuliefern, das zurzeit die Kontrolle innehat.
5.8.2 Continuations
Eine Continuation ist eine weitere komplexe und obskure Kontrollstruktur, die die meisten Programmierer niemals nutzen werden. Continuations existieren in Form der
Kernel
-Methode
callcc
und des
Continuation
-Objekts. Continuations sind in Ruby 1.8 Teil der Kernplattform, aber in Ruby 1.9 wurden sie durch Fiber ersetzt und in die Standardbibliothek verschoben. Um sie in Ruby 1.9 zu verwenden, müssen Sie sie wie folgt explizit importieren:
require 'continuation'
Implementierungsschwierigkeiten hindern andere Implementierungen von Ruby (etwa JRuby, die Java-basierte
Weitere Kostenlose Bücher