Zuweisung

In Ruby wird die Zuweisung mit dem Zeichen = (Gleichheitszeichen) durchgeführt. Dieses Beispiel weist der lokalen Variablen v die Zahl fünf zu.

v = 5

Eine Zuweisung erstellt eine lokale Variable, falls die Variable zuvor nicht referenziert wurde.

Das Ergebnis eines Zuweisungsausdrucks ist immer der zugewiesene Wert, einschließlich Zuweisungsmethoden.

Namen lokaler Variablen

Ein Name für eine lokale Variable muss mit einem Kleinbuchstaben des US-ASCII-Zeichensatzes oder einem Zeichen mit gesetztem achten Bit beginnen. Typischerweise sind lokale Variablen US-ASCII-kompatibel, da die Tasten zum Tippen dieser Zeichen auf allen Tastaturen vorhanden sind.

(Ruby-Programme müssen in einem US-ASCII-kompatiblen Zeichensatz geschrieben werden. In solchen Zeichensätzen zeigt das Setzen des achten Bits ein erweitertes Zeichen an. Ruby erlaubt lokalen Variablen, solche Zeichen zu enthalten.)

Ein Name für eine lokale Variable kann Buchstaben, Zahlen, einen Unterstrich _ oder ein Zeichen mit gesetztem achten Bit enthalten.

Gültigkeitsbereich lokaler Variablen

Sobald einem Namen einer lokalen Variablen etwas zugewiesen wurde, werden alle Verwendungen des Namens für den Rest des Gültigkeitsbereichs als lokale Variablen betrachtet.

Hier ist ein Beispiel

1.times do
  a = 1
  puts "local variables in the block: #{local_variables.join ", "}"
end

puts "no local variables outside the block" if local_variables.empty?

Dies gibt aus:

local variables in the block: a
no local variables outside the block

Da ein Block einen neuen Gültigkeitsbereich erstellt, "lecken" keine lokalen Variablen, die darin erstellt wurden, in den umgebenden Gültigkeitsbereich.

Variablen, die in einem äußeren Gültigkeitsbereich definiert sind, erscheinen im inneren Gültigkeitsbereich.

a = 0

1.times do
  puts "local variables: #{local_variables.join ", "}"
end

Dies gibt aus:

local variables: a

Sie können Variablen in einem Block vom äußeren Gültigkeitsbereich isolieren, indem Sie sie nach einem ; in den Argumenten des Blocks auflisten. Sehen Sie die Dokumentation für blocklokale Variablen in der Dokumentation zum Aufrufen von Methoden für ein Beispiel.

Siehe auch Kernel#local_variables, aber beachten Sie, dass eine for-Schleife keinen neuen Gültigkeitsbereich erstellt, wie es ein Block tut.

Lokale Variablen und Methoden

In Ruby sind die Namen lokaler Variablen und Methodennamen fast identisch. Wenn Sie einem dieser mehrdeutigen Namen nichts zugewiesen haben, geht Ruby davon aus, dass Sie eine Methode aufrufen möchten. Sobald Sie dem Namen etwas zugewiesen haben, geht Ruby davon aus, dass Sie eine lokale Variable referenzieren möchten.

Die lokale Variable wird erstellt, wenn der Parser die Zuweisung antrifft, nicht wenn die Zuweisung stattfindet.

a = 0 if false # does not assign to a

p local_variables # prints [:a]

p a # prints nil

Die Ähnlichkeit zwischen Methoden- und lokalen Variablennamen kann zu verwirrendem Code führen, zum Beispiel:

def big_calculation
  42 # pretend this takes a long time
end

big_calculation = big_calculation()

Jetzt wird jede Referenz auf big_calculation als lokale Variable betrachtet und gecacht. Um die Methode aufzurufen, verwenden Sie self.big_calculation.

Sie können einen Methodenaufruf erzwingen, indem Sie leere Klammern für Argumente verwenden, wie oben gezeigt, oder indem Sie einen expliziten Empfänger wie self verwenden. Die Verwendung eines expliziten Empfängers kann eine NameError auslösen, wenn die Sichtbarkeit der Methode nicht öffentlich ist oder der Empfänger das Literal self ist.

Ein weiterer häufig verwirrender Fall ist die Verwendung eines Modifikators if:

p a if a = 0.zero?

Anstatt "true" auszugeben, erhalten Sie eine NameError: "undefined local variable or method ‘a’". Da Ruby das nackte a links vom if zuerst parst und noch keine Zuweisung zu a gesehen hat, geht es davon aus, dass Sie eine Methode aufrufen möchten. Ruby sieht dann die Zuweisung an a und geht davon aus, dass Sie eine lokale Variable referenzieren.

Die Verwirrung entsteht durch die nicht sequenzielle Ausführung des Ausdrucks. Zuerst wird die lokale Variable zugewiesen, dann versuchen Sie, eine nicht existierende Methode aufzurufen.

Lokale Variablen und eval

Die Verwendung von eval zur Auswertung von Ruby-Code ermöglicht den Zugriff auf lokale Variablen, die im selben Gültigkeitsbereich definiert sind, auch wenn die lokalen Variablen erst nach dem Aufruf von eval definiert werden. Lokale Variablen, die innerhalb des Aufrufs von eval definiert sind, werden jedoch nicht im umgebenden Gültigkeitsbereich reflektiert. Innerhalb des Aufrufs von eval sind lokale Variablen, die im umgebenden Gültigkeitsbereich definiert sind, und lokale Variablen, die innerhalb des Aufrufs von eval definiert sind, zugänglich. Sie können jedoch nicht auf lokale Variablen zugreifen, die in vorherigen oder nachfolgenden Aufrufen von eval im selben Gültigkeitsbereich definiert wurden. Betrachten Sie jeden eval-Aufruf als einen separaten verschachtelten Gültigkeitsbereich. Beispiel:

def m
  eval "bar = 1"
  lvs = eval "baz = 2; ary = [local_variables, foo, baz]; x = 2; ary"
  eval "quux = 3"
  foo = 1
  lvs << local_variables
end

m
# => [[:baz, :ary, :x, :lvs, :foo], nil, 2, [:lvs, :foo]]

Instanzvariablen

Instanzvariablen werden für alle Methoden desselben Objekts gemeinsam genutzt.

Eine Instanzvariable muss mit einem @ ("at-Zeichen" oder kommerzielles at) beginnen. Andernfalls folgen Instanzvariablennamen den Regeln für lokale Variablennamen. Da die Instanzvariable mit einem @ beginnt, kann das zweite Zeichen ein Großbuchstabe sein.

Hier ist ein Beispiel für die Verwendung von Instanzvariablen:

class C
  def initialize(value)
    @instance_variable = value
  end

  def value
    @instance_variable
  end
end

object1 = C.new "some value"
object2 = C.new "other value"

p object1.value # prints "some value"
p object2.value # prints "other value"

Eine nicht initialisierte Instanzvariable hat den Wert nil.

Die Methode value hat Zugriff auf den von der Methode initialize gesetzten Wert, jedoch nur für dasselbe Objekt.

Klassenvariablen

Klassenvariablen werden zwischen einer Klasse, ihren Unterklassen und ihren Instanzen gemeinsam genutzt.

Eine Klassenvariable muss mit @@ (zwei "at-Zeichen") beginnen. Der Rest des Namens folgt den gleichen Regeln wie bei Instanzvariablen.

Hier ist ein Beispiel

class A
  @@class_variable = 0

  def value
    @@class_variable
  end

  def update
    @@class_variable = @@class_variable + 1
  end
end

class B < A
  def update
    @@class_variable = @@class_variable + 2
  end
end

a = A.new
b = B.new

puts "A value: #{a.value}"
puts "B value: #{b.value}"

Dies gibt aus:

A value: 0
B value: 0

Weiter mit demselben Beispiel können wir mit Objekten aus beiden Klassen aktualisieren, und der Wert wird gemeinsam genutzt.

puts "update A"
a.update

puts "A value: #{a.value}"
puts "B value: #{b.value}"

puts "update B"
b.update

puts "A value: #{a.value}"
puts "B value: #{b.value}"

puts "update A"
a.update

puts "A value: #{a.value}"
puts "B value: #{b.value}"

Dies gibt aus:

update A
A value: 1
B value: 1
update B
A value: 3
B value: 3
update A
A value: 4
B value: 4

Der Zugriff auf eine nicht initialisierte Klassenvariable löst eine NameError-Ausnahme aus.

Beachten Sie, dass Klassen Instanzvariablen haben, da Klassen Objekte sind. Versuchen Sie also, Klassen- und Instanzvariablen nicht zu verwechseln.

Globale Variablen

Globale Variablen sind überall zugänglich.

Globale Variablen beginnen mit einem $ (Dollarzeichen). Der Rest des Namens folgt den gleichen Regeln wie bei Instanzvariablen.

Hier ist ein Beispiel

$global = 0

class C
  puts "in a class: #{$global}"

  def my_method
    puts "in a method: #{$global}"

    $global = $global + 1
    $other_global = 3
  end
end

C.new.my_method

puts "at top-level, $global: #{$global}, $other_global: #{$other_global}"

Dies gibt aus:

in a class: 0
in a method: 0
at top-level, $global: 1, $other_global: 3

Eine nicht initialisierte globale Variable hat den Wert nil.

Ruby hat einige spezielle globale Variablen, die sich je nach Kontext unterschiedlich verhalten, wie z. B. die regulären Ausdruck-Matchvariablen oder solche, die beim Zuweisen Nebeneffekte haben. Details finden Sie in der Dokumentation der globalen Variablen.

Zuweisungsmethoden

Sie können Methoden definieren, die sich wie eine Zuweisung verhalten, zum Beispiel:

class C
  def value=(value)
    @value = value
  end
end

c = C.new
c.value = 42

Die Verwendung von Zuweisungsmethoden lässt Ihre Programme schöner aussehen. Bei der Zuweisung zu einer Instanzvariable verwenden die meisten Leute Module#attr_accessor.

class C
  attr_accessor :value
end

Bei der Verwendung von Methoden-Zuweisung müssen Sie immer einen Empfänger haben. Wenn Sie keinen Empfänger haben, geht Ruby davon aus, dass Sie einer lokalen Variablen etwas zuweisen.

class C
  attr_accessor :value

  def my_method
    value = 42

    puts "local_variables: #{local_variables.join ", "}"
    puts "@value: #{@value.inspect}"
  end
end

C.new.my_method

Dies gibt aus:

local_variables: value
@value: nil

Um die Zuweisungsmethode zu verwenden, müssen Sie den Empfänger festlegen.

class C
  attr_accessor :value

  def my_method
    self.value = 42

    puts "local_variables: #{local_variables.join ", "}"
    puts "@value: #{@value.inspect}"
  end
end

C.new.my_method

Dies gibt aus:

local_variables:
@value: 42

Beachten Sie, dass der von einer Zuweisungsmethode zurückgegebene Wert ignoriert wird, da das Ergebnis eines Zuweisungsausdrucks immer der zugewiesene Wert ist.

Abgekürzte Zuweisung

Sie können mehrere Operatoren und die Zuweisung mischen. Um einem Objekt 1 zu addieren, können Sie schreiben:

a = 1

a += 2

p a # prints 3

Dies ist gleichbedeutend mit

a = 1

a = a + 2

p a # prints 3

Sie können die folgenden Operatoren auf diese Weise verwenden: +, -, *, /, %, **, &, |, ^, <<, >>

Es gibt auch ||= und &&=. Ersteres weist zu, wenn der Wert nil oder false war, während letzteres zuweist, wenn der Wert nicht nil oder false war.

Hier ist ein Beispiel

a ||= 0
a &&= 1

p a # prints 1

Beachten Sie, dass diese beiden Operatoren sich eher wie a || a = 0 verhalten als wie a = a || 0.

Implizite Array-Zuweisung

Sie können implizit ein Array erstellen, indem Sie mehrere Werte bei der Zuweisung auflisten:

a = 1, 2, 3

p a # prints [1, 2, 3]

Dies erstellt implizit ein Array.

Sie können * oder den "Splat"-Operator verwenden, um ein Array beim Zuweisen zu entpacken. Dies ähnelt der Mehrfachzuweisung:

a = *[1, 2, 3]

p a # prints [1, 2, 3]

b = *1

p b # prints [1]

Sie können den Splat-Operator an beliebiger Stelle auf der rechten Seite der Zuweisung verwenden:

a = 1, *[2, 3]

p a # prints [1, 2, 3]

Mehrfachzuweisung

Sie können mehrere Werte auf der rechten Seite mehreren Variablen zuweisen:

a, b = 1, 2

p a: a, b: b # prints {:a=>1, :b=>2}

In den folgenden Abschnitten kann an jeder Stelle, an der "Variable" verwendet wird, auch eine Zuweisungsmethode, eine Instanz-, Klassen- oder globale Variable verwendet werden.

def value=(value)
  p assigned: value
end

self.value, $global = 1, 2 # prints {:assigned=>1}

p $global # prints 2

Sie können die Mehrfachzuweisung verwenden, um zwei Werte vor Ort zu vertauschen:

old_value = 1

new_value, old_value = old_value, 2

p new_value: new_value, old_value: old_value
# prints {:new_value=>1, :old_value=>2}

Wenn Sie auf der rechten Seite der Zuweisung mehr Werte haben als Variablen auf der linken Seite, werden die zusätzlichen Werte ignoriert.

a, b = 1, 2, 3

p a: a, b: b # prints {:a=>1, :b=>2}

Sie können * verwenden, um zusätzliche Werte auf der rechten Seite der Zuweisung zu sammeln.

a, *b = 1, 2, 3

p a: a, b: b # prints {:a=>1, :b=>[2, 3]}

Das * kann sich an jeder Stelle auf der linken Seite befinden.

*a, b = 1, 2, 3

p a: a, b: b # prints {:a=>[1, 2], :b=>3}

Aber Sie dürfen nur ein * in einer Zuweisung verwenden.

Array-Zerlegung

Ähnlich wie bei der Array-Zerlegung in Methodenargumenten können Sie ein Array während der Zuweisung mithilfe von Klammern zerlegen:

(a, b) = [1, 2]

p a: a, b: b # prints {:a=>1, :b=>2}

Sie können ein Array als Teil einer größeren Mehrfachzuweisung zerlegen.

a, (b, c) = 1, [2, 3]

p a: a, b: b, c: c # prints {:a=>1, :b=>2, :c=>3}

Da jede Zerlegung als eigene Mehrfachzuweisung betrachtet wird, können Sie * verwenden, um Argumente in der Zerlegung zu sammeln.

a, (b, *c), *d = 1, [2, 3, 4], 5, 6

p a: a, b: b, c: c, d: d
# prints {:a=>1, :b=>2, :c=>[3, 4], :d=>[5, 6]}