Die Programmiersprache Ruby (German Edition)
trägt wie eine existierende Variable, weisen Aufrufe des Blocks der bestehenden Variablen einfach einen neuen Wert zu, anstatt eine neue, blocklokale Variable zu erzeugen. Der folgende Code ist beispielsweise problematisch, weil er denselben Bezeichner
i
als Block-Parameter für zwei verschachtelte Blöcke verwendet:
1.upto(10) do |i| # 10 Zeilen
1.upto(10) do |i| # Jede hat 10 Spalten
print "#{i} " # Spaltennummer ausgeben
end
# Versuch, Zeilennummer auszugeben, liefert Spaltennummer
print " ==> Spalte #{i}\n"
end
Ruby 1.9 ist anders: Block-Parameter sind immer lokal für ihren Block, und Aufrufe des Blocks weisen bestehenden Variablen niemals Werte zu. Wenn Ruby 1.9 mit dem Flag
-w
aufgerufen wird, erhalten Sie eine Warnung, wenn ein Block-Parameter denselben Namen hat wie eine existierende Variable. Dies hilft Ihnen bei der Vermeidung von Code, der in 1.8 und 1.9 unterschiedlich arbeitet.
Ruby 1.9 verhält sich auch in einem wichtigen Punkt anders. Die Blocksyntax wurde so erweitert, dass Sie Ihnen erlaubt, blocklokale Variablen zu deklarieren, die garantiert lokal sind, selbst wenn bereits eine Variable mit demselben Namen im umgebenden Gültigkeitsbereich existiert. Setzen Sie dazu ein Semikolon und eine kommaseparierte Liste blocklokaler Variablen hinter die Liste der Block-Parameter. Hier sehen Sie ein Beispiel:
x = y = 0 # Lokale Variablen
1.upto(4) do |x;y| # x und y sind lokal im Block
# x und y "überschatten" die äußeren Variablen
y = x + 1 # y als Notizvariable verwenden
puts y*y # Gibt 4, 9, 16, 25 aus
end
[x,y] # => [0,0]: Block ändert sie nicht
In diesem Code ist
x
ein Block-Parameter: Er erhält einen Wert, wenn der Block mit
yield
aufgerufen wird.
y
ist eine Block-lokale Variable. Sie erhält keinen Wert aus einem
yield
-Aufruf, sondern hat den Wert
nil
, bis der Block ihr tatsächlich irgendeinen anderen Wert zuweist. Der Zweck der Deklaration dieser Block-lokalen Variablen besteht darin, zu garantieren, dass Sie nicht versehentlich den Wert irgendeiner existierenden Variablen ändern. (Das kann beispielsweise passieren, wenn ein Block über die Zwischenablage von einer Methode in die andere kopiert wird.) Wenn Sie Ruby 1.9 mit der Option
-w
aufrufen, erhalten Sie eine Warnung, falls eine Block-lokale Variable eine existierende Variable überschattet.
Blöcke können natürlich mehr als einen Parameter und mehr als eine lokale Variable enthalten. Hier sehen Sie einen Block mit zwei Parametern und drei lokalen Variablen:
hash.each {|key,value; i,j,k| ... }
5.4.4 Argumente an einen Block übergeben
Wir haben schon erwähnt, dass die Parameter eines Blocks eine starke Ähnlichkeit zu den Parametern einer Methode aufweisen. Sie sind jedoch streng genommen nicht identisch. Die Argumentwerte, die auf ein
yield
-Schlüsselwort folgen, werden den Block-Parametern aufgrund von Regeln zugewiesen, die eher den Regeln für Variablenwertzuweisungen entsprechen als denjenigen für Methodenaufrufe. Wenn ein Iterator also
yield k,v
ausführt, um einen mit den Parametern
|key, value|
deklarierten Block aufzurufen, dann ist das äquivalent zu folgender Wertzuweisungsanweisung:
key,value = k,v
Der Iterator
Hash.each_pair
übergibt dem Block wie folgt ein Schlüssel/Wert-Paar: [ 24 ]
{:eins=>1}.each_pair {|key,value| ... } # key=:eins, value=1
In Ruby 1.8 wird es sogar noch klarer, dass Blockaufrufe die Variablenwertzuweisung verwenden. Denken Sie daran, dass Parameter in Ruby 1.8 nur dann lokal für den Block sind, wenn sie nicht bereits als lokale Variablen der enthaltenden Methode im Einsatz sind. Wenn sie bereits lokale Variablen sind, wird ihnen einfach ein Wert zugewiesen. In der Tat erlaubt Ruby 1.8 die Verwendung jeder beliebigen Art von Variablen als Block-Parameter, einschließlich globalen Variablen und Instanzvariablen:
{:one=>1}.each_pair {|$key, @value| ... } # Funktioniert in Ruby 1.9 nicht mehr
Dieser Iterator setzt die globale Variable
$key
auf
:eins
und die Instanzvariable
@value
auf 1. Wie bereits erwähnt, macht Ruby 1.9 Block-Parameter Block-lokal. Das bedeutet auch, dass Block-Parameter keine globalen Variablen oder Instanzvariablen mehr sein können.
Der Iterator
Hash.each
übergibt Schlüssel/Wert-Paare als zwei Elemente eines einzelnen Array. Allerdings kommt Code wie dieser häufig vor:
hash.each {|k,v| ... } # key und value werden Parametern k und v zugewiesen.
Dies funktioniert auch mit paralleler Wertzuweisung. Der
yield
-Wert, ein Array mit zwei Argumenten, wird
Weitere Kostenlose Bücher