Wie verwenden Sie globale Variablen oder konstante Werte in Ruby?

68

Ich habe ein Programm, das aussieht wie:

$offset = Point.new(100, 200);

def draw(point)
  pointNew = $offset + point;
  drawAbsolute(point)
end

draw(Point.new(3, 4));

Die Verwendung von $offsetscheint ein bisschen komisch.

Wenn ich in C etwas außerhalb einer Funktion definiere, ist es automatisch eine globale Variable. Warum muss es in Ruby sein $offset, kann offsetund kann aber nicht global sein? Wenn ja offset, dann ist es ein Einheimischer? Aber lokal wo, weil es sich sehr global anfühlt.

Gibt es bessere Möglichkeiten, den obigen Code zu schreiben? Die Verwendung von $offsetmag zunächst etwas hässlich erscheinen.


Update: Ich kann diesen Offset in eine classDefinition einfügen, aber was ist, wenn zwei oder mehrere Klassen diese Konstante verwenden müssen? Muss ich in diesem Fall noch ein definieren $offset?

Unpolarität
quelle
25
Wenn Sie von C kommen, wissen Sie das vielleicht nicht, aber Sie müssen in Ruby keine Semikolons am Ende Ihrer Zeilen einfügen. Sie müssen nur verwenden; mehrere Anweisungen in derselben Zeile zu trennen, z. B. "a = 5; b = 10"
mikej

Antworten:

53

Eine Sache, die Sie erkennen müssen, ist, dass in Ruby alles ein Objekt ist. Wenn Sie Ihre Methoden nicht in Moduleoder definieren Class, wird sie von Ruby in die ObjectKlasse eingefügt. Ihr Code ist also lokal für den ObjectBereich.

Ein typischer Ansatz für die objektorientierte Programmierung besteht darin, die gesamte Logik innerhalb einer Klasse zu kapseln:

class Point
  attr_accessor :x, :y

  # If we don't specify coordinates, we start at 0.
  def initialize(x = 0, y = 0)
    # Notice that `@` indicates instance variables.
    @x = x
    @y = y
  end

  # Here we override the `+' operator.
  def +(point)
    Point.new(self.x + point.x, self.y + point.y)
  end

  # Here we draw the point.
  def draw(offset = nil)
    if offset.nil?
      new_point = self
    else
      new_point = self + offset 
    end
    new_point.draw_absolute
  end

  def draw_absolute
    puts "x: #{self.x}, y: #{self.y}"
  end
end

first_point = Point.new(100, 200)
second_point = Point.new(3, 4)

second_point.draw(first_point)

Hoffe das klärt ein bisschen.

Igor
quelle
+1 für das Refactoring. In Bezug auf die Top-Level - Code zu dem Sein lokalen ObjectBereich: die für Variablen ohne das stimmt $Präfix, aber im Gegensatz dazu Methoden Sie dort gesetzt sind global (während auch private Mitglieder der immer ObjectKlasse).
mklement0
Ich denke also, in meinem Fall, wenn sich alle PointObjekte auf einen bestimmten konstanten Versatz beziehen, kann ich einen Versatz (x, y) als Klassenkonstante haben, so dass der draw_absolute()diese Klassenkonstante als Versatz verwendet
Nichtpololarität
112

Der variable Umfang in Ruby wird bis zu einem gewissen Grad durch Siegel gesteuert. Variablen, die mit beginnen, $sind global, Variablen mit @sind Instanzvariablen, @@bedeuten Klassenvariablen und Namen, die mit einem Großbuchstaben beginnen, sind Konstanten. Alle anderen Variablen sind Einheimische. Wenn Sie eine Klasse oder Methode öffnen, ist dies ein neuer Bereich, und im vorherigen Bereich verfügbare Lokale sind nicht verfügbar.

Ich ziehe es im Allgemeinen vor, das Erstellen globaler Variablen zu vermeiden. Es gibt zwei Techniken, die im Allgemeinen den gleichen Zweck erreichen, den ich für sauberer halte:

  1. Erstellen Sie eine Konstante in einem Modul. In diesem Fall würden Sie also alle Klassen, die den Offset benötigen, in das Modul einfügen Foound eine Konstante erstellen Offset, damit alle Klassen darauf zugreifen können Foo::Offset.

  2. Definieren Sie eine Methode für den Zugriff auf den Wert. Sie können die Methode global definieren, aber ich denke, es ist besser, sie in ein Modul oder eine Klasse zu kapseln. Auf diese Weise sind die Daten dort verfügbar, wo Sie sie benötigen, und Sie können sie bei Bedarf sogar ändern. Die Struktur Ihres Programms und der Besitz der Daten werden jedoch klarer. Dies entspricht eher den OO-Designprinzipien.

Futter
quelle
10

Einer der Gründe, warum die globale Variable ein Präfix benötigt (als "Siegel" bezeichnet), ist, dass Sie in Ruby im Gegensatz zu C Ihre Variablen nicht deklarieren müssen, bevor Sie sie zuweisen. Das Siegel wird verwendet, um den Umfang der Variablen explizit darzustellen.

Ohne ein bestimmtes Präfix für Globals bezieht sich eine angegebene Anweisung pointNew = offset + pointin Ihrer drawMethode offsetauf eine lokale Variable in der Methode (und führt NameErrorin diesem Fall zu einer). Das Gleiche gilt für @Verweisvariablen und @@für Klassenvariablen.

In anderen Sprachen , die expliziten Erklärungen wie verwenden C, Javausw. die Platzierung der Deklaration wird verwendet , um den Umfang zu steuern.

mikej
quelle
2
Woher weiß die $Hilfe von Ruby, ob es sich um eine neue oder eine vorhandene Variable handelt? Wenn Sie dem $fooGedanken etwas zuweisen, dass es etwas gibt $foo, aber es passiert, dass es noch nichts $foogibt oder es nur von jemandem entfernt wurde, wird Ruby $footrotzdem als neues globales Objekt erstellt, obwohl es nicht dasselbe ist, das $fooSie gedacht haben. $wird das Fehlen ordnungsgemäßer Erklärungen nicht beheben.
SasQ
-3

Ich denke, es ist lokal für die Datei, die Sie als Offset deklariert haben. Betrachten Sie jede Datei als eine Methode für sich.

Vielleicht das Ganze in eine Klasse einordnen und dann Offset zu einer Klassenvariablen machen mit @@offset = Point.new(100, 200);?

Gábor Hargitai
quelle
3
Nein, eine lokale Variable ist nicht lokal für eine Datei, sondern für den innersten Block, in dem sie deklariert ist.
Teemu Leisti