Dove e come viene specificata la variabile _ (sottolineatura)?

La maggior parte è consapevole del significato speciale di _ in IRB come titolare per l’ultimo valore di ritorno, ma non è quello che sto chiedendo qui.

Invece, sto chiedendo di _ quando usato come nome di una variabile in plain-old-Ruby-code. Qui sembra avere un comportamento speciale, simile a una “non badare alla variabile” (à la Prolog ). Ecco alcuni esempi utili che illustrano il suo comportamento unico:

 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 

Questi sono stati tutti eseguiti direttamente in Ruby (con puts s aggiunto in) -non IRB-per evitare conflitti con le sue funzionalità aggiuntive.

Questo è tutto frutto della mia stessa sperimentazione, dato che non trovo documentazione su questo comportamento da nessuna parte (ovviamente non è la cosa più facile da cercare). Alla fine, sono curioso di sapere come tutto funzioni internamente, così posso capire meglio cosa è speciale in _ . Quindi chiedo riferimenti alla documentazione e, preferibilmente, il codice sorgente di Ruby (e forse RubySpec ) che rivelano come si comporta _ in Ruby.

Nota: la maggior parte di questo è nata da questa discussione con @Niklas B.

C’è una certa gestione speciale nell’origine per sopprimere l’errore “nome dell’argomento duplicato”. Il messaggio di errore appare solo in shadowing_lvar_gen all’interno di parse.y , la versione 1.9.3 ha il seguente aspetto :

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

e idUScore è definito in id.c come questo:

 REGISTER_SYMID(idUScore, "_"); 

Vedrai una gestione speciale simile in 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])); } } 

Noterai che l'avviso è soppresso sulla seconda riga del ciclo for .

L'unica gestione speciale di _ che ho trovato nella sorgente 1.9.3 è sopra: l'errore del nome duplicato è soppresso e l'avviso della variabile inutilizzata è soppresso. Oltre a queste due cose, _ è semplicemente una vecchia variabile semplice come qualsiasi altra. Non conosco alcuna documentazione sulla (minore) particolarità di _ .

In Ruby 2.0, il idUScore == v[i] in warn_unused_var viene sostituito con una chiamata 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 sopprime gli avvisi per le variabili che iniziano con _ :

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

piuttosto che solo _ stesso. Quindi 2.0 allenta un po 'le cose.

_ è un identificatore valido. Gli identificatori non possono contenere solo caratteri di sottolineatura, ma possono anche essere un trattino basso.

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

Puoi anche usarlo come nome del metodo:

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

Ovviamente, non è esattamente un nome leggibile, né passa alcuna informazione al lettore su cosa si riferisca alla variabile o su cosa faccia il metodo.

IRB , in particolare, imposta _ sul valore dell’ultima espressione:

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

Come è nel codice sorgente , imposta semplicemente _ sull’ultimo valore:

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

Ha esplorato qualche repository. Ecco cosa ho trovato:

Sull’ultima riga del file id.c , c’è la chiamata:

 REGISTER_SYMID(idUScore, "_"); 

grep l’origine di idUScore mi ha dato due risultati apparentemente rilevanti:

  • Nella funzione shadowing_lvar_gen
  • Nella funzione warn_unused_var

shadowing_lvar_gen sembra essere il meccanismo attraverso il quale il parametro formale di un blocco sostituisce una variabile con lo stesso nome che esiste in un altro ambito. È la funzione che sembra generare “nome argomento duplicato” SyntaxError e l’avviso “ombreggiatura esterna variabile locale”.

Dopo aver grep il sorgente per shadowing_lvar_gen , ho trovato quanto segue sul log delle modifiche per Ruby 1.9.3 :

Mar 11 dic 01:21:21 2007 Yukihiro Matsumoto

  • parse.y (shadowing_lvar_gen): nessun errore duplicato per “_”.

Quale è probabilmente l’origine di questa linea :

 if (idUScore == name) return name; 

Da ciò deduco che in una situazione come proc { |_, _| :x }.call :a, :b proc { |_, _| :x }.call :a, :b , una variabile _ semplicemente ombreggia l’altra.


Ecco il commit in questione . Fondamentalmente ha introdotto queste due linee:

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

Da un momento in cui idUScore non esisteva nemmeno, apparentemente.