2012-03-04 15 views
73

La mayoría conoce _’s special meaning in IRB as a holder for last return value, pero eso es no lo que estoy preguntando aquí.¿Dónde y cómo se especifica la variable _ (guión bajo)?

En su lugar, estoy preguntando acerca de _ cuando se utiliza como nombre de variable en plain-old-Ruby-code. Aquí parece tener un comportamiento especial, similar a una variable "do not care" (a la Prolog). Estos son algunos ejemplos útiles que ilustran su comportamiento único:

lambda { |x, x| 42 }   # SyntaxError: duplicated argument name 
lambda { |_, _| 42 }.call(4, 2) # => 42 
lambda { |_, _| 42 }.call(_, _) # NameError: undefined local variable or method `_' 
lambda { |_| _ + 1 }.call(42) # => 43 
lambda { |_, _| _ }.call(4, 2) # 1.8.7: => 2 
           # 1.9.3: => 4 
_ = 42 
_ * 100   # => 4200 
_, _ = 4, 2; _ # => 2 

Estos fueron todos corren en Ruby directamente (con puts s añaden en) -no IRB-para evitar conflictos con su funcionalidad adicional.

Esto es todo un resultado de mi propia experimentación, ya que no puedo encontrar ninguna documentación sobre este comportamiento en ningún lado (sin duda, no es lo más fácil de buscar). En definitiva, tengo curiosidad de cómo todo esto funciona internamente, así puedo entender mejor qué es lo especial de _. Así que estoy pidiendo referencias a documentación y, preferiblemente, el código fuente de Ruby (y tal vez RubySpec) que revela cómo se comporta _ en Ruby.

Nota: la mayor parte de esta surgió de this discussion con @Niklas B.

Respuesta

48

Hay un manejo especial en la fuente para suprimir el error de "nombre de argumento duplicado". El mensaje de error aparece únicamente en el interior de shadowing_lvar_genparse.y, the 1.9.3 version looks like this:

static ID 
shadowing_lvar_gen(struct parser_params *parser, ID name) 
{ 
    if (idUScore == name) return name; 
    /* ... */ 

y idUScore es defined in id.c así:

REGISTER_SYMID(idUScore, "_"); 

Verás un manejo especial similar en warn_unused_var:

static void 
warn_unused_var(struct parser_params *parser, struct local_vars *local) 
{ 
    /* ... */ 
    for (i = 0; i < cnt; ++i) { 
     if (!v[i] || (u[i] & LVAR_USED)) continue; 
     if (idUScore == v[i]) continue; 
     rb_compile_warn(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i])); 
    } 
} 

Notará que la advertencia se suprime en la segunda línea de t él for loop.

El único tratamiento especial de _ que pude encontrar en la fuente 1.9.3 es anterior: se suprime el error de nombre duplicado y se suprime la advertencia de la variable no utilizada.Aparte de esas dos cosas, _ es simplemente una vieja variable simple como cualquier otra. No conozco ninguna documentación sobre la especialidad (menor) de _.

en Ruby 2.0, la prueba idUScore == v[i] en warn_unused_var se sustituye con una llamada a is_private_local_id:

if (is_private_local_id(v[i])) continue; 
rb_warn4S(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i])); 

y is_private_local_id suprime advertencias para las variables que comienzan con _:

if (name == idUScore) return 1; 
/* ... */ 
return RSTRING_PTR(s)[0] == '_'; 

en lugar de sólo _ sí mismo. Entonces 2.0 libera las cosas un poco.

+1

Me pregunto si la diferencia en el comportamiento de 'lambda {| _, _ | _} .call (4, 2) 'entre 1.8 y 1.9 es simplemente un efecto colateral involuntario? Como en circunstancias "normales" donde el nombre de la variable no se puede duplicar, el orden en el que se asignan es irrelevante. –

+0

Wow, me ganaste. +1 –

+3

@AndrewMarshall: Sí, creo que el problema "4 vs 2" es solo un artefacto de cómo el 1.8 y 1.9 manejan la pila. El único momento en que sería notable es '| _, _, ... |' porque el error duplicado se ha suprimido. –

22

_ es un identificador válido. Los identificadores no pueden contener guiones bajos, también pueden ser y subrayar.

_ = o = Object.new 
_.object_id == o.object_id 
# => true 

También se puede utilizar como nombres de método:

def o._; :_ end 
o._ 
# => :_ 

Por supuesto, no es exactamente un nombre legible, ni tampoco pasar cualquier información al lector acerca de lo que la variable se refiere o lo que hace el método

IRB, en particular, establece _ al valor de la última expresión:

$ irb 
> 'asd' 
# => "asd" 
> _ 
# => "asd" 

Como es in the source code, sino que simplemente establece _ al último valor:

@workspace.evaluate self, "_ = IRB.CurrentContext.last_value" 

Hizo algún repositorio explorando.Esto es lo que encontré:

En las últimas líneas del archivo id.c, existe la llamada:

REGISTER_SYMID(idUScore, "_"); 

grep ing la fuente de idUScore me dio dos resultados aparentemente relevantes:

shadowing_lvar_gen parece ser el mecanismo a través del cual el parámetro formal de un bloque reemplaza una variable del mismo nombre que existe en otro ámbito. Es la función que parece generar el "nombre de argumento duplicado" SyntaxError y la advertencia "sombrear variable local externa".

Después grep ing la fuente de shadowing_lvar_gen, encontré la siguiente on the changelog for Ruby 1.9.3:

Mar Dic 11 01:21:21 2007 Yukihiro Matsumoto

  • parse.y (shadowing_lvar_gen): no duplicado error para "_".

¿Qué es probable que sea el origen de this line:

if (idUScore == name) return name; 

partir de esto, deduzco que en una situación como proc { |_, _| :x }.call :a, :b, uno _ variables simplemente sombras del otro.


Aquí está the commit in question. Básicamente se introdujo estas dos líneas:

if (!uscore) uscore = rb_intern("_"); 
if (uscore == name) return; 

Desde el momento en que idUScore ni siquiera existían, al parecer.

+5

Esto no explica * en absoluto * por qué 'lambda {| _, _ | 42} 'funciona mientras' lambda {| x, x | 42} 'no. –

+0

@AndrewMarshall, parece que tienes razón. '| _, _ |' funciona, pero '| __, __ |' no funciona. '_' parece tener un significado especial, veré si puedo extraer información de la fuente de Ruby. –

+1

Por cierto, su actualización, si bien es informativa, no es relevante, ya que solo se aplica a IRb, lo cual expresé específicamente en mi pregunta sobre la que no estaba preguntando. –

Cuestiones relacionadas