2012-05-28 9 views

Respuesta

12

El motivo ff es inaccesible dentro de la definición del método test es simplemente que los métodos (creados con la palabra clave def) crean un nuevo ámbito. Lo mismo ocurre con la definición de clases y módulos utilizando las palabras clave class y module, respectivamente.

El papel de main (el objeto de nivel superior) es casi completamente irrelevante para la cuestión del alcance en esta situación.

Tenga en cuenta que, si usted quiere que su método test para tener acceso a los locales definidos en el contexto definición, a continuación, utilizar el define_method (o en su caso, el método define_singleton_method), ver aquí:

ff = "hi" 
define_singleton_method("test") { ff } 
test #=> "hi" 

A diferencia de la palabra clave def, la familia de métodos define_method no crea nuevos ámbitos, sino que cierra el ámbito actual y captura las variables locales.

La razón usando @ff trabajó en el siguiente ejemplo dado por @soup, no es que main es de alguna manera un "caso especial" es sólo que una Ivar definido en el nivel superior es una Ivar de main y así se puede acceder a una método invocado en main.

¿Cuál es, sin embargo, la relación del método test con main?No es un método en solo main en sí mismo - en realidad es un método de instancia privado definido en la clase Object. Esto significa que el método test estaría disponible (como método privado) para casi todos los objetos en su programa Ruby. Todos los métodos definidos en el nivel superior (main) en realidad se definen como métodos de instancia privada en la clase Object.

Para obtener más información sobre el Rubí de nivel superior, ver este artículo: http://banisterfiend.wordpress.com/2010/11/23/what-is-the-ruby-top-level/

+0

Gracias por esto. Entiendo cómo funciona main ahora. ¡Qué bien que 'MyObject.new .__ send __ (: test)' funcione! (Aunque tal vez no sea útil). Re: 'Test1Fixed' Pensé que sí explique que la varilla' @ ff' es un miembro de la clase, de ahí que sea nula, 'Test2' se suponía que explicara esto por su salida (" 'will/NO emitirá 'instance'' "dependiendo de si llama a' test' o 'class_test') pero quizás no estaba particularmente claro. Además, si escribiste/empezaste a hacer palanca, ¡es genial hermano! – Soup

+0

@soup actualizó la respuesta para reflejar que usted explicó correctamente '@ ff', lo siento :) Thx re pry, me alegro de que le guste :) – horseyguy

9

Ruby scope es simple y complejo.

Primero debe recordar que todo es un objeto y que todo tiene un alcance.

Para responder a su pregunta directamente, main es un objeto y por ejemplo cuando se escribe def x... se está definiendo un método en el objeto main

bajo observación en pir/IRB:

# note the error describes 
[1] pry(main)> main 'main:Object' 
NameError: undefined local variable or method 'main' for main:Object 
from (pry):1:in '<main>' 

# self is the current object, which is main 
[2] pry(main)> self 
=> main 

# note main is an object of type Object 
[3] pry(main)> self.class 
=> Object 

# main has methods 
[4] pry(main)> self.methods 
=> [:to_s, :public, etc etc] 

Así que cuando se escribir

ff = "ff" 
def test 
    puts ff 
end 

lo que está haciendo en realidad es

class main 
    ff = "ff" 
    def test 
     puts ff 
    end 
end 

Lo cual no funciona porque ff está fuera del alcance. Para solucionar esto, debe convertir ff en una variable de instancia, así que anteponga su nombre con @. El siguiente trabajo:

@ff = "ff" 
def test 
    puts @ff 
end 

test 

Tenga en cuenta que esto parece ser un caso especial para main que las clases regulares, ver más abajo.

Si tenemos nuestra propia clase de prueba:

class Test1 
    ff = "ff" 
    def test 
     puts ff 
    end 
end 

Test1.new.test # undefined variable/method 'ff' error 

Esta falla porque ff no está definido en el ámbito correcto como se esperaría. En su lugar, se establece el alcance cuando el analizador está ejecutando nuestra declaración de clase.

Así que vamos a tratar la revisión anterior:

class Test1Fixed 
    @ff = "ff" 
    def test 
     puts @ff 
    end 
end 

Test1Fixed.new.test # results in blank line 

Eso es raro.

vamos a añadir este método:

def ff? 
    puts "ff is: #{@ff.nil? ? "nil" : "not nill"}" 
end 

Test1Fixed.new.ff? # => ff is: nil 

¿Por qué es @ff nula?

El siguiente podría hacer esto más claro:

class Test2 
    puts "Where am i?" 
    @instance = "instance" 

    def initialize 
     puts "Initalize" 
    end 

    puts "pants on" 

    def test 
     puts "This will NOT output 'instance': #{@instance}" 
    end 

    def self.class_test 
     puts "This WILL output 'instance': #{@instance}" 
    end 
end 

puts "Creating new Test2 Object" 
t = Test2.new 
puts "Calling test on Test2 Object" 
t.test 

puts "Calling class_test on Test2 Class object" 
Test2.class_test 

La ejecución de este obtenemos el siguiente resultado:

$ ruby scope.rb 
Where am i? 
pants on 
Creating new Test2 Object 
Initalize 
Calling test on Test2 Object 
This will NOT output 'instance': 
Calling class_test on Test2 Class object 
This WILL output 'instance': instance 

Como se puede ver, el interperter corre sobre nuestra declaración de la clase con el fin, justo como en cualquier otro lugar y da salida a nuestras declaraciones puts. Esto se vuelve más interesante cuando comenzamos a llamar a los métodos.Escribir @instance = ... es lo mismo que escribir self.instance = ..., así que ¿puedes adivinar dónde hemos definido la instancia? Sí, en el objeto de clase Test2 (no en un objeto Test2).

Por eso, cuando llamamos test, nada se outputed, porque dentro test, self se refiere a la Test2 objeto instanciado, no el Test2 class object (que es donde nos propusimos @instance a ser cualquier cosa!). Es por eso que configura sus variables de instancia dentro de initialize, donde self apuntará al objeto real.

Se puede ver cuando definimos un método de clase a través self.class_test y luego llamar a ese método en la clase de objeto Prueba2 (Test2.class_test), obtenemos el resultado esperado, ya que en ese ámbito, @instance se definió.

Espero que esto tenga algún sentido.

La sección de alcance http://rubykoans.com/ puede ayudar a consolidar parte de este conocimiento.

+0

No, cuando define un método en el nivel superior (principal), en realidad está definiendo un método de instancia privada en la clase 'Object'. La única forma de definir un método en 'main' es definir un método en la clase singleton de' main', a la: 'def self.hello; end' – horseyguy

+0

En realidad, volver a leer su respuesta, la mayor parte es completamente incorrecta jeje. – horseyguy

+0

¿Podría editarlo o dirigirme a donde puedo obtener más información? No he estudiado un tema en particular (principal, etc., he estudiado ámbitos/objetos de rubí, aunque tal vez no en su mayor grado), solo reuní lo que sabía/pensaba que sabía sobre objetos/alcance de rubí. – Soup

2

En ruby, un nombre sin un sigil siempre tiene alcance "local". Ruby utiliza el alcance léxico, por lo que dentro de un método, el nombre siempre se refiere a ese método solamente.

Los otros ámbitos son global, instancia y clase. Aquí hay un buen documento sobre ellos: http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Operators#Scope

Hay algunas maneras de resolver su problema; mi favorito sería pasar lo que se imprime como un argumento:

ff = "ff" 
def test(ff) 
    puts ff 
end 

Lo más probable es que estaría encapsular lo que estás haciendo en una clase y el uso de una variable de instancia.

También podría usar una variable global ($ ff), pero de esa manera la locura miente.

+0

Si solo desea compartir una variable en el archivo actual, es mejor usar '@ a' que' $ a', ya que podría evitar la contaminación del espacio de nombres en algunos ampliar. –

0

Why can't I access a local variable inside a method in Ruby?

Debido a que es una variable local. Las variables locales son locales para el ámbito en el que están definidas. Es por eso que se llaman "variables locales", después de todo.

En este caso, ha definido una variable local en el ámbito del script y, por lo tanto, está visible en el ámbito del script, y en ningún otro lugar.

Cuestiones relacionadas