2012-05-04 24 views
7

que estaba bajo la impresión de que las definiciones de clase en Ruby pueden ser reabiertas:Las variables locales en las definiciones de clase/ámbito

class C 
    def x 
    puts 'x' 
    end 
end 

class C 
    def y 
    puts 'y' 
    end 
end 

Esto funciona como se esperaba y se añade a la definición y clase original.

estoy confundido en cuanto a por qué el código siguiente no funciona como se esperaba:

class D 
    x = 12 
end 

class D 
    puts x 
end 

Esto dará lugar a una excepción de NameError. ¿Por qué hay un nuevo ámbito local iniciado cuando se reabre una clase? Esto parece un poco contradictorio. ¿Hay alguna manera de continuar el alcance local anterior cuando se extiende una clase?

+0

Esta es una buena pregunta por su propia cuenta, pero hay una razón por la que desea Haga esto en lugar de almacenar una variable de instancia en el objeto de la clase en sí (es decir, reemplace 'x' con' @ x' arriba). – Phrogz

+0

@Phrogz No, no hay ninguna razón por la que quisiera hacer esto en el código de producción. Le pido a este que rasque un picor intelectual en lugar de resolver un problema del mundo real. – Matty

Respuesta

7

Las variables locales no están asociados con un objeto, que están asociados con un alcance.

Las variables locales tienen un alcance léxico. Lo que estamos tratando de hacer es no más válido que:

def foo 
    x = :hack if false # Ensure that x is a local variable 
    p x if $set   # Won't be called the first time through 
    $set = x = 42  # Set the local variable and a global flag 
    p :done 
end 

foo     #=> :done 

foo     #=> nil (x does not have the value from before) 
         #=> done 

En lo anterior es el mismo método, en el mismo objeto, que está siendo ejecutado en ambas ocasiones. El self no ha cambiado. Sin embargo, las variables locales se borran entre invocaciones.

Volver a abrir la clase es como volver a invocar un método: está en el mismo ámbito self, pero está comenzando un nuevo contexto local. Cuando cierra el bloque class D con end, sus variables locales se descartan (a menos que fueran closed over).

6

En ruby, las variables locales son accesibles solo en el ámbito que están definidas. Sin embargo, las palabras clave class provocan un alcance completamente nuevo.

class D 
    # One scope 
    x = 12 
end 

class D 
    # Another scope 
    puts x 
end 

Por lo tanto, no se puede obtener acceso a la variable local que se define en la primera sección de class, porque cuando salga primero alcance, la variable local dentro de él se destruye y la memoria es liberado por la recolección de basura.

Esto también es cierto para el def, por ejemplo.

+2

¿No es esto solo repetir la pregunta? Sí, es un alcance diferente, el OP lo reconoció. ¿Pero por qué? – AShelly

+1

Gracias, hice una adición a mi respuesta. – Flexoid

0

La solución fácil sería hacer x a class instance variable. Por supuesto, esto no responde a su pregunta de alcance. Creo que la razón x no está en el scope (ver Detectando el alcance de una variable Rubí) de la clase volvió a abrir se debe a su alcance nunca fue la de la clase D en el primer lugar. El alcance de x finalizó cuando la clase D cerrada porque x no es una variable de instancia ni una variable de clase.

Espero que ayude.

+0

No es cierto. Pruebe 'x = 42; clase Foo; x = 17; p x; fin; p x' y observe que ve '17' y luego' 42'. Al abrir una clase, se inicia un nuevo contexto local. – Phrogz

0

Una parte de la razón por la cual este comportamiento tiene sentido se encuentra en las capacidades de metaprogramación; usted podría estar usando algunos variables temporales para almacenar los datos que se puede utilizar para nombrar una nueva constante, o hacer referencia a un nombre de método:

class A 
    names, values = get_some_names_and_values() 

    names.each_with_index do |name, idx| 
    const_set name, value[idx] 
    end 
end 

O tal vez, usted necesita para obtener los eigenclass ...

class B 
    eigenclass = class << self; self; end 
    eigenclass.class_eval do 
    ... 
    end 
end 

tiene sentido tener un nuevo ámbito de aplicación cada vez que se vuelve a abrir una clase, porque en Rubí a menudo se escribe código dentro de un definiton clase que es código real para ser ejecutado y apertura una clase es simplemente una manera de conseguir el valor correcto para self. Usted no quiere que el contenido de la clase a ser contaminado por estas variables, otherwhise que tendría que utilizar una variable de instancia de clase:

class C 
    @answer = 42 
end 

class C 
    p @answer 
end 
Cuestiones relacionadas