Alle Originalinhalte werden auf Ukrainisch erstellt. Noch nicht alle Inhalte wurden übersetzt. Einige Beiträge sind möglicherweise nur auf Ukrainisch verfügbar.Mehr erfahren

Sichtbarkeit des lokalen Variablenbereichs in Ruby

Beitrags-Cover: Sichtbarkeit des lokalen Variablenbereichs in Ruby
Dieser Inhalt wurde automatisch aus dem Ukrainischen übersetzt.
In Ruby wird ein neuer Sichtbarkeitsbereich für lokale Variablen an mehreren Stellen erstellt. Man muss diese Stellen verstehen und lernen.
  • globaler Kontext (main);
  • Klassen- (oder Modul-) Definition;
  • Methoden-Definition;
Betrachten wir einige Beispiele von der Website ruby-lang.org und einige neue.
Erstellen wir eine Datei example.rb mit dem Code

Globaler Sichtbarkeitsbereich (main)

# Eine globale Variable wird außerhalb von Klassen, Methoden oder Blöcken definiert.
var = 1  # Globale Variable

class Demo
  var = 2  # Klassenvariable

  def method
    var = 3  # Lokale Methode-Variable
    puts "in method: var = #{var}"
  end

  puts "in class: var = #{var}"
end

puts "at top level: var = #{var}"
Demo.new.method
Die Ausführung des Skripts 'ruby example.rb' zeigt uns folgendes Ergebnis:
# in class: var = 2
# at top level: var = 1
# in method: var = 3
Beachten Sie, dass der Code innerhalb der Klassendefinition während seiner Erstellung ausgeführt wird, weshalb die Meldung innerhalb der Klasse sofort erscheint.

Blöcke in Ruby und deren Sichtbarkeit

In Ruby erzeugen Blöcke ({ ... } oder do ... end) fast einen neuen Sichtbarkeitsbereich. Das bedeutet, dass lokale Variablen, die innerhalb eines Blocks definiert sind, normalerweise außerhalb des Blocks nicht zugänglich sind. Es gibt jedoch einige Besonderheiten. Beachten Sie das Wort 'fast', es wird auf der Seite der Ruby-Sprache verwendet und manchmal von Anfängern falsch interpretiert.

Beispiel mit einem Block

a = 0
1.upto(3) do |i|
  a += i
  b = i * i
end
puts a  # => 6
puts b  # Es wird ein Fehler auftreten, da b außerhalb des Blocks nicht definiert ist
In diesem Beispiel wird die Variable a, die vor dem Block definiert wurde (Konstruktion do ... end), innerhalb des Blocks modifiziert, und diese Änderungen sind außerhalb des Blocks sichtbar. Auf der anderen Seite ist die Variable b, die innerhalb des Blocks definiert wurde, außerhalb nicht zugänglich.
Blöcke fast erzeugen einen neuen Sichtbarkeitsbereich, da lokale Variablen, die innerhalb des Blocks definiert sind, von außen nicht zugänglich sind. Wenn jedoch eine Variable bereits im äußeren Sichtbarkeitsbereich existiert, bevor man in den Block eintritt, wird sie auch innerhalb des Blocks verfügbar sein, und Änderungen an ihr bleiben nach dem Verlassen des Blocks erhalten.
Warum "fast"? Das Wort "fast" wird aus mehreren Gründen verwendet (die bereits zuvor beschrieben wurden):
  • Variablen, die vor dem Block definiert werden, können innerhalb des Blocks zugänglich und modifiziert werden. Änderungen an ihnen bleiben nach dem Verlassen des Blocks erhalten.
  • Variablen, die zum ersten Mal innerhalb des Blocks definiert werden, sind außerhalb des Blocks nicht zugänglich.
Diese Nuancen sind besonders wichtig zu beachten, wenn man mit Threads und asynchronem Code arbeitet, wo die Sichtbarkeit die Zugänglichkeit von Variablen zwischen verschiedenen Teilen des Codes beeinflussen kann.
Betrachten wir ein weiteres Beispiel für ein besseres Verständnis:
x = 10

[1, 2, 3].each do |i|
  x += i
  y = i * 2
end

puts x  # => 16 (die Variable x wird innerhalb des Blocks verändert)
puts y  # Es wird ein Fehler auftreten, da y außerhalb des Blocks nicht definiert ist
Der Fehler für 'puts y' wird folgender sein:
(irb):8:in `<main>': undefined local variable or method `y' for main:Object (NameError)

puts y
     ^
	from /Users/user/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/irb-1.13.1/exe/irb:9:in `<top (required)>'
	from /Users/user/.rbenv/versions/3.2.1/bin/irb:25:in `load'
	from /Users/user/.rbenv/versions/3.2.1/bin/irb:25:in `<main>'
In diesem Beispiel wird die Variable x vor dem Block definiert und innerhalb des Blocks modifiziert. Die Variable y wird innerhalb des Blocks definiert und ist außerhalb nicht zugänglich. Dies veranschaulicht, wie Blöcke "fast" einen neuen Sichtbarkeitsbereich erzeugen, aber nicht ganz den Zugriff auf Variablen trennen, die bereits im äußeren Sichtbarkeitsbereich existieren.

Beispiel mit Threads

threads = []

["one", "two"].each do |name|
  threads << Thread.new do
    local_name = name
    a = 0
    3.times do |i|
      Thread.pass
      a += i
      puts "#{local_name}: #{a}"
    end
  end
end

threads.each { |t| t.join }
Das Ergebnis in meinem Fall (das Ergebnis hängt von der Ausführung von Thread.pass, dem Betriebssystem und der CPU ab):
one: 0
two: 0
one: 1
one: 3
two: 1
two: 3
Wir erstellen ein leeres Array threads, in dem wir alle erstellten Threads speichern werden. Die each-Schleife durchläuft das Array der Strings ["one", "two"], wobei jedes Element nacheinander in der Variablen name verfügbar ist. Für jedes Element des Arrays wird ein neuer Thread mit Thread.new erstellt. Der Code innerhalb des Blocks do ... end wird im Kontext des neuen Threads ausgeführt.
Die Variable local_name erhält den Wert des aktuellen Elements des Arrays name. Dies wird getan, damit jeder Thread seine eigene lokale Kopie der Variable name hat. Dann wird eine lokale Variable a mit dem Startwert 0 erstellt. Anschließend läuft die Schleife. Die Schleife wird dreimal ausgeführt. Bei jeder Iteration haben wir folgende Aktionen:
  • Thread.pass wird ausgeführt, was anderen Threads erlaubt, ausgeführt zu werden.
  • Die Variable a wird um den Wert i erhöht.
  • Der Wert local_name und der aktuelle Wert a werden ausgegeben.
Und wir beenden:
threads.each { |t| t.join }
Der join-Befehl zwingt den Hauptthread, auf den Abschluss jedes der erstellten Threads zu warten. Dies ist notwendig, damit alle Threads ihre Arbeit beenden, bevor das Programm (Skript) beendet wird.

Kontrollstrukturen, Methoden und deren Sichtbarkeiten

Im Folgenden werden Beispiele für Kontrollstrukturen und Methoden zur Veranschaulichung der Funktionsweise von Sichtbarkeiten aufgeführt. Ruby hat eine Vielzahl von Kontrollstrukturen und Methoden, die den Fluss der Programmausführung steuern.
if / elsif / else
if condition
  # code
elsif another_condition
  # anderer code
else
  # anderer code
end
unless
unless condition
  # code
end
case / when
case variable
when value1
  # code
when value2
  # anderer code
else
  # anderer code
end
while
while condition
  # code
end
until
until condition
  # code
end
for
for element in collection
  # code
end
loop
loop do
  # code
  break if condition
end
begin / rescue / ensure / else
begin
  # code
rescue SomeException => e
  # Ausnahmebehandlung
ensure
  # code, der immer ausgeführt wird
else
  # code, der ausgeführt wird, wenn keine Ausnahme auftritt
end
redo
for i in 0..5
  retry if i > 2
  puts "i: #{i}"
end
retry
begin
  # code
rescue
  retry
end
next
for i in 0..5
  next if i < 3
  puts "i: #{i}"
end
break
for i in 0..5
  break if i > 2
  puts "i: #{i}"
end
Kontrollstrukturen (if, while, for, usw.) erzeugen keinen neuen Sichtbarkeitsbereich, sodass lokale Variablen innerhalb von ihnen im umgebenden Kontext verfügbar sind.
Beispiele für Methoden:
times
5.times do |i|
  puts i
end
upto
1.upto(5) do |i|
  puts i
end
downto
5.downto(1) do |i|
  puts i
end
step
0.step(10, 2) do |i|
  puts i
end
each
[1, 2, 3].each do |element|
  puts element
end
map
result = [1, 2, 3].map do |element|
  element * 2
end
puts result
select
result = [1, 2, 3, 4, 5].select do |element|
  element.even?
end
puts result
reject
result = [1, 2, 3, 4, 5].reject do |element|
  element.even?
end
puts result
find
result = [1, 2, 3, 4, 5].find do |element|
  element.even?
end
puts result
inject/reduce
sum = [1, 2, 3, 4, 5].inject(0) do |accumulator, element|
  accumulator + element
end
puts sum
Methoden (times, each, usw.) akzeptieren häufig Blöcke, die einen neuen Sichtbarkeitsbereich für Variablen schaffen können, die innerhalb des Blocks definiert sind.
Ich habe absichtlich viele Beispiele für Kontrollstrukturen und Methoden hinzugefügt, um zu zeigen, dass man hier leicht einen Fehler machen und ein Problem mit der Sichtbarkeit bekommen kann. Das Hauptziel ist nicht, sich alle Methoden zu merken, sondern den Unterschied zwischen Kontrollstrukturen und Methoden zu verstehen. Dieses Wissen wird helfen, Fehler zu debuggen in potenziell problematischen Stellen im Code.
Um anschaulich zu zeigen, wie das alles funktioniert, schreiben wir Tests:
require 'rspec'

RSpec.describe 'Sichtbarkeiten in Ruby' do
  context 'Kontrollstrukturen' do
    it 'erzeugt einen neuen Sichtbarkeitsbereich mit if/elsif/else' do
      if true
        var = 1
      end
      expect(var).to eq(1)
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit unless' do
      unless false
        var = 2
      end
      expect(var).to eq(2)
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit case/when' do
      case 1
      when 1
        var = 3
      end
      expect(var).to eq(3)
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit while' do
      i = 0
      while i < 1
        var = 4
        i += 1
      end
      expect(var).to eq(4)
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit until' do
      i = 0
      until i > 0
        var = 5
        i += 1
      end
      expect(var).to eq(5)
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit for' do
      for i in 0..0
        var = 6
      end
      expect(var).to eq(6)
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit loop' do
      var = nil
      loop do
        var = 7
        break
      end
      expect(var).to eq(7)
    end

    it 'behandelt eine Ausnahme mit begin/rescue/ensure' do
      var = 0
      begin
        raise 'Fehler'
      rescue
        var = 1
      ensure
        var += 2
      end
      expect(var).to eq(3)
    end

    it 'behandelt eine Ausnahme mit begin/rescue/else/ensure' do
      var = 0
      begin
        var += 1
      rescue
        var += 2
      else
        var += 3
      ensure
        var += 4
      end
      expect(var).to eq(8)
    end

    it 'wiederholt die Ausführung mit redo' do
      var = 0
      i = 0
      for i in 0..5
        if i < 2
          var = i
          break if i == 1 # Vermeidet eine Endlosschleife
        end
      end
      expect(var).to eq(1)
    end

    it 'wiederholt die Ausführung mit retry' do
      var = 0
      attempts = 0
      begin
        raise 'Fehler' if attempts < 1
      rescue
        attempts += 1
        retry if attempts < 2
      else
        var = 9
      end
      expect(var).to eq(9)
    end

    it 'überspringt eine Iteration mit next' do
      var = []
      for i in 0..5
        next if i < 3
        var << i
      end
      expect(var).to eq([3, 4, 5])
    end

    it 'verlässt die Schleife mit break' do
      for i in 0..5
        break if i > 2
        var = i
      end
      expect(var).to eq(2)
    end
  end

  context 'Methoden' do
    it 'erzeugt einen neuen Sichtbarkeitsbereich mit times' do
      1.times do
        var = 10
      end
      expect(defined?(var)).to be_nil
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit upto' do
      1.upto(1) do
        var = 11
      end
      expect(defined?(var)).to be_nil
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit downto' do
      1.downto(1) do
        var = 12
      end
      expect(defined?(var)).to be_nil
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit step' do
      0.step(0, 1) do
        var = 13
      end
      expect(defined?(var)).to be_nil
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit each' do
      [1].each do
        var = 14
      end
      expect(defined?(var)).to be_nil
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit map' do
      [1].map do
        var = 15
      end
      expect(defined?(var)).to be_nil
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit select' do
      [1].select do
        var = 16
      end
      expect(defined?(var)).to be_nil
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit reject' do
      [1].reject do
        var = 17
      end
      expect(defined?(var)).to be_nil
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit find' do
      [1].find do
        var = 18
      end
      expect(defined?(var)).to be_nil
    end

    it 'erzeugt einen neuen Sichtbarkeitsbereich mit inject/reduce' do
      [1].inject(0) do |acc, _|
        var = 19
      end
      expect(defined?(var)).to be_nil
    end
  end
end
Alle Tests wurden erfolgreich ausgeführt:
Finished in 0.06456 seconds (files took 0.26825 seconds to load)
23 examples, 0 failures

Dieser Beitrag hat noch keine Ergänzungen vom Autor.

29. Mai, 09:09 Uhr

Welche Betriebssysteme unterstützen Ruby?

meme code
meme code@memecode
Erstellt Ruby eine neue Kopie des Objekts, wenn eine Variable einer anderen Variable zugewiesen wird?
29. Mai, 09:30 Uhr

Erstellt Ruby eine neue Kopie des Objekts, wenn eine Variable einer anderen Variable zugewiesen wird?

meme code
meme code@memecode
Was ist der Unterschied zwischen immediate value und reference in Ruby?
29. Mai, 12:00 Uhr

Was ist der Unterschied zwischen immediate value und reference in Ruby?

meme code
meme code@memecode
Warum gibt der Ruby-Code nil zurück, nachdem puts ausgeführt wurde?
29. Mai, 20:30 Uhr

Warum gibt der Ruby-Code nil zurück, nachdem puts ausgeführt wurde?

meme code
meme code@memecode
Was ist der Unterschied zwischen nil und false in Ruby?
29. Mai, 20:59 Uhr

Was ist der Unterschied zwischen nil und false in Ruby?

meme code
meme code@memecode
Warum ist eine leere Zeichenkette (string) in Ruby nicht false?
31. Mai, 14:39 Uhr

Warum ist eine leere Zeichenkette (string) in Ruby nicht false?

meme code
meme code@memecode
Was ist der Unterschied zwischen int und bigint in Ruby? Minimale und maximale Werte.
13. Jun, 06:37 Uhr

Was ist der Unterschied zwischen int und bigint in Ruby? Minimale und maximale Werte.

meme code
meme code@memecode
Was bedeutet der Fehler 'is out of range' in Ruby on Rails? Bereichsfehler - Ganzzahl mit einer Begrenzung von 4 Bytes
13. Jun, 07:18 Uhr

Was bedeutet der Fehler 'is out of range' in Ruby on Rails? Bereichsfehler - Ganzzahl mit einer Begrenzung von 4 Bytes

meme code
meme code@memecode
Was sind Immutabilität und Mutabilität?
19. Jun, 07:48 Uhr

Was sind Immutabilität und Mutabilität?

meme code
meme code@memecode
[Ruby] Was ergibt die Addition von 10,5 und 10?
23. Jun, 13:23 Uhr

[Ruby] Was ergibt die Addition von 10,5 und 10?

meme code
meme code@memecode
[Ruby] Was unterscheidet Variablen, die mit @, @@ und $ beginnen?
23. Jun, 14:00 Uhr

[Ruby] Was unterscheidet Variablen, die mit @, @@ und $ beginnen?

meme code
meme code@memecode
Was ist eine Funktion in der Programmierung?
24. Jun, 18:15 Uhr

Was ist eine Funktion in der Programmierung?

meme code
meme code@memecode