¿Dónde y cómo se especifica la variable _ (guión bajo)?

La mayoría conoce el significado especial de IRB como titular del último valor de retorno, pero eso no es lo que estoy preguntando aquí.

En cambio, estoy preguntando por _ cuando se usa como nombre de variable en el código antiguo de Ruby. Aquí parece tener un comportamiento especial, similar a una variable “no importa” (à la Prolog ). Aquí hay 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 

Todos estos fueron ejecutados directamente en Ruby (con puts añadidas) -no IRB-para evitar conflictos con su funcionalidad adicional.

Sin embargo, todo esto es resultado de mi propia experimentación, ya que no puedo encontrar documentación sobre este comportamiento en ningún lado (no es lo más fácil 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 la documentación y, preferiblemente, el código fuente de Ruby (y tal vez RubySpec ) que revela cómo _ comporta en Ruby.

Nota: la mayoría de esto surgió de esta discusión con @Niklas B.

Hay un manejo especial en la fuente para suprimir el error de “nombre de argumento duplicado”. El mensaje de error solo aparece en parse.y dentro de parse.y , la versión 1.9.3 se ve así :

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

e idUScore se define en id.c esta manera:

 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 del bucle for .

El único manejo especial de _ que pude encontrar en la fuente 1.9.3 es el siguiente: 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 sencilla como cualquier otra. No conozco ninguna documentación sobre la especialidad (menor) de _ .

En Ruby 2.0, la idUScore == v[i] en warn_unused_var se reemplaza por 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])); 

e is_private_local_id suprime las advertencias de las variables que comienzan con _ :

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

en lugar de solo _ mismo. Entonces 2.0 libera las cosas un poco.

_ es un identificador válido. Los identificadores no pueden contener guiones bajos, sino que también pueden ser un guión bajo.

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

También puedes usarlo como nombres de métodos:

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

Por supuesto, no es exactamente un nombre legible, ni transmite ninguna información al lector sobre a qué se refiere la variable o qué hace el método.

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

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

Como está en el código fuente , simplemente establece _ hasta el último valor:

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

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

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

 REGISTER_SYMID(idUScore, "_"); 

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

  • En la función shadowing_lvar_gen
  • En la función warn_unused_var

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 ” SyntaxError nombre de argumento duplicado” y la advertencia de “variable local externa SyntaxError “.

Después de grep la fuente para shadowing_lvar_gen , encontré lo siguiente en el registro de cambios para Ruby 1.9.3 :

Martes 11 de diciembre 01:21:21 2007 Yukihiro Matsumoto

  • parse.y (shadowing_lvar_gen): no hay error duplicado para “_”.

Cuál es probable que sea el origen de esta línea :

 if (idUScore == name) return name; 

A partir de esto, deduzco que en una situación como proc { |_, _| :x }.call :a, :b proc { |_, _| :x }.call :a, :b , una _ variable simplemente sombrea a la otra.


Aquí está el compromiso en cuestión . Básicamente introdujo estas dos líneas:

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

Desde un momento en que idUScore ni siquiera existía, al parecer.