2010-11-11 13 views
47

En este ejemplo,No entiendo rubí ámbito local

def foo(x) 
    if(x > 5) 
    bar = 100 
    end 
    puts bar 
end 

Entonces foo (6) Salidas: 100 y fu (3) Salidas nada.

Sin embargo, si he cambiado la definición de

def foo(x) 
    if(x > 5) 
    bar = 100 
    end 
    puts bob 
end 

consigo un error "variable local o método no definido".

Así que mi pregunta es ¿por qué no recibo este error cuando llamo a foo (3) y la barra nunca se establece?

Respuesta

53

Hay un par de cosas pasando aquí. En primer lugar, las variables declaradas dentro del bloque if tienen el mismo alcance local que las variables declaradas en el nivel superior del método, por lo que bar está disponible fuera del if. En segundo lugar, está recibiendo ese error porque bob se está haciendo referencia de la nada. El intérprete de Ruby nunca lo ha visto y nunca lo ha visto inicializado. Sin embargo, se ha visto bar inicializado antes, dentro de la sentencia if. Entonces cuando se pone en la barra, sabe que existe. Combina esos dos y esa es tu respuesta.

+2

Gracias, sí, entendí dónde estaba el error de bob simplemente no estaba seguro de por qué no recibía un error de barra. ¿Sabes si puedo confiar en este comportamiento, es parte de la especificación? Por ejemplo, ¿puedo verificar la barra para nil después de la instrucción if o debo declarar también explícitamente bar = nil antes de la instrucción if? – rebo

+1

Sí, este es un comportamiento confiable. Declarar bar = nil seguramente sería más explícito. Pero no sé que la mayoría de los rubyistas harían eso, sin embargo. Si mantiene sus métodos pequeños, no debería ser difícil de entender o ver de dónde viene el bar. – Todd

0

No estoy seguro de lo que está preguntando. Ejecutar foo(3) con la segunda definición siempre dará un error, ya que bob nunca se define. El argumento para el método no cambia eso.

3

Así que no tome esto como un evangelio (ya que se basa más en la observación que en la comprensión), pero parece que el intérprete de rubíes marcará cualquier palabra (sin un símbolo delante) a la izquierda de un igual firmar como local Su ejemplo es extraña, esto es aún más extraño

def foo 
    bar = bar 
    puts bar // nil, which gets coerced into "" 
end 

No entiendo por qué o cómo funciona, pero hay que tenerlo.

+1

+1! JavaScript funciona de la misma manera. Mueve todas las declaraciones a la parte superior y deja las asignaciones donde están. Esto puede ser un poco confuso. – jwueller

+2

Todo en el LHS de una asignación se inicializará en nil: '>> si es falso; prueba = lo que sea; fin ; test # => nil'. También es así como funcionan cosas como 'x || = 5', porque significa' x = x || 5' que será 'x = nil || 5' en caso de que x no haya sido definido previamente. –

+0

@jwueller Ruby no mueve todas las asignaciones a la parte superior. 'pone b; b = b; 'da como resultado ' 'NameError: variable local indefinida o método' b 'para main: Object'' – Ajedi32

14

Su segundo ejemplo es en realidad una pista falsa: la razón por la que está recibiendo una excepción no es porque bob no está inicializado, es porque es ambiguo. Es imposible decir si es una variable o un método.

Su primer ejemplo funciona, porque las variables locales no inicializadas (así como las variables globales y las variables de instancia) se evalúan a nil. Por lo tanto, puts bar está perfectamente bien: en un caso bar se inicializa en 100 y esto se evalúa como 100, en el otro caso no está inicializado y por lo tanto se evalúa como nil. puts llama a to_s en su argumento, que se define para nil (simplemente devuelve la cadena vacía), por lo que todo está bien y elegante.

Ver también In Ruby, why after starting irb, foo.nil? says undefined error, and @foo.nil? gives “true”, and @@wah.nil? gives error again?

+0

No sé Ruby, pero si esto es correcto, entonces la respuesta aceptada por Todd probablemente no es correcta. Él implica que el problema con bob no es que sea nulo, sino que no está inicializado. Las otras respuestas sugieren que estás en lo correcto. – mickeyf

+0

@ Jörg: Muchas gracias, ahora me lo has dejado muy claro. Me pregunto si Matz está demasiado ocupado para aclarar esto como lo explicaste ahora. ¡Gran respuesta! –

2

foo(3) no hace nada sin salida. Muestra una nueva línea.

Usando inspect le daría más de una pista:

def foo(x) 
    if(x > 5) 
    bar = 100 
    end 
    puts bar.inspect 
end 

foo(3) 

imprime

nil 

bar es una variable de pleno derecho, que sólo pasa a tener un valor de nil.