2009-06-25 19 views
64

Tengo un programa que se parece a:¿Cómo se usan variables globales o valores constantes en Ruby?

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

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

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

el uso de $offset parece un poco raro.

En C, si defino algo fuera de cualquier función, que es una variable global de forma automática. ¿Por qué en Ruby tiene que ser $offset pero no puede ser offset y seguir siendo global? Si es offset, ¿es local? Pero local a dónde, porque se siente mucho global.

¿Hay mejores maneras de escribir el código anterior? El uso de $offset puede parecer un poco feo al principio.


Actualización: Me puedo poner este desplazamiento dentro de una definición class, pero lo que si dos o varias clases necesitan utilizar esta constante? En este caso, ¿todavía necesito definir un $offset?

+24

Si viene de C, puede que no sepa esto, pero no necesita poner puntos y coma al final de sus líneas en Ruby. Solo necesitas usar; para separar múltiples declaraciones en la misma línea, p. "a = 5; b = 10" – mikej

Respuesta

53

Una cosa que debes tener en cuenta es que en Ruby todo es un objeto. Dado que, si no define sus métodos dentro de Module o Class, Ruby lo colocará dentro de la clase Object. Por lo tanto, su código será local para el alcance Object.

Un enfoque típico de programación orientada a objetos es encapsular toda la lógica dentro de una clase:

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) 

la esperanza que esto aclare un poco.

+0

+1 para la refactorización. Con respecto al código de nivel superior que es local para el alcance 'Objeto': eso es cierto para variables sin el prefijo '$', pero, por el contrario, _methods_ colocas allí _are_ global (mientras que también se vuelven miembros privados de la clase 'Object') – mklement0

+0

entonces creo que en mi caso, si todos los objetos 'Point' están con respecto a un cierto desplazamiento constante, entonces puedo tener un desplazamiento (x, y) como una constante de clase, por lo que' draw_absolute() 'usará esta clase constante como un desplazamiento –

-2

creo que es local para el archivo que declaró offset. Considera que cada archivo es un método en sí mismo.

tal vez poner todo esto en una clase y luego hacer un desplazamiento variable de clase con @@offset = Point.new(100, 200);?

+1

No, una variable local es ** no ** local a un archivo, pero al bloque más interno en el que se declara. –

9

Una de las razones por las que la variable global necesita un prefijo ($) es porque en Ruby, a diferencia de C, no es necesario declarar sus variables antes de asignarlas sin un prefijo específico para globales dado un declaración como offset = Point.new(100, 200) dentro de su método de dibujo, entonces Ruby no sabría si se estaba refiriendo a la variable existente o creando una nueva variable local dentro de su método. Lo mismo con el prefijo @ para variables de instancia.

+2

¿Cómo ayuda el '$' a Ruby a saber si esta es una nueva variable o una existente? Si asigna algo a '$ foo' pensando que es un' $ foo' existente, pero sucede que todavía no hay '$ foo', o que hubo alguien que lo eliminó, entonces Ruby creará' $ foo' 'como un nuevo global, sin embargo, a pesar de que no es el mismo' $ foo' que has pensado. '$' no reparará la falta de declaraciones adecuadas. – SasQ

+3

Está diciendo que los sigilos variables de Ruby ('$', '@', '@@') son necesarios para _prevenir sombreado inadvertido_ de variables de un alcance superior. Sin embargo, es _imposible_ sombras de variables en Ruby, porque las variables (sin sigilos) son siempre _exclusivamente locales para el alcance que están definidas en_ (que es el bloque más interno, las excepciones limitadas son bloques de código, que crean cierres sobre el local alcance). Por lo tanto, el problema no es uno de [falta de] _declaración_, sino de _scope_: los sigilos son necesarios para hacer referencia a variables de/crear variables en un ámbito (especial) _ que no sea el local_. – mklement0

107

alcance variable en Ruby es controlado por sigilos en algún grado. Las variables que comienzan con $ son globales, las variables con @ son variables de instancia, @@ significa variables de clase y los nombres que comienzan con una letra mayúscula son constantes. Todas las demás variables son locales. Cuando abre una clase o método, ese es un nuevo ámbito, y los locales disponibles en el alcance anterior no están disponibles.

por lo general prefieren evitar la creación de variables globales. Hay dos técnicas que generalmente logran el mismo propósito que considero más limpio:

  1. Crear una constante en un módulo. Entonces, en este caso, pondría todas las clases que necesitan el desplazamiento en el módulo Foo y creará una constante Offset, para que todas las clases puedan acceder al Foo::Offset.

  2. Define un método para acceder al valor. Puede definir el método globalmente, pero nuevamente, creo que es mejor encapsularlo en un módulo o clase. De esta forma, los datos estarán disponibles donde los necesite e incluso podrá modificarlos si lo necesita, pero la estructura de su programa y la propiedad de los datos serán más claros. Esto está más en línea con los principios de diseño de OO.