2012-03-04 17 views
73

Molti sono a conoscenza di _’s special meaning in IRB as a holder for last return value, ma quello è non quello che sto chiedendo qui.Dove e come viene specificata la variabile _ (trattino basso)?

Invece, sto chiedendo di _ quando viene usato come nome di variabile in plain-old-Ruby-code. Qui sembra avere un comportamento speciale, simile a una variabile "non importa" (à la Prolog). Ecco alcuni esempi utili che illustrano il 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 erano tutti fuga rubino direttamente (con puts s aggiunti) -non IRB-per evitare conflitti con la sua funzionalità aggiuntive.

Questo è tutto un risultato della mia stessa sperimentazione però, poiché non riesco a trovare alcuna documentazione su questo comportamento da nessuna parte (ovviamente non è la cosa più facile da cercare). In fin dei conti, sono curioso di sapere come tutto funzioni internamente, così posso capire meglio cosa è speciale su _. Quindi sto chiedendo riferimenti alla documentazione e, preferibilmente, al codice sorgente di Ruby (e forse allo RubySpec) che rivela come _ si comporta in Ruby.

Nota: la maggior parte di questo nata da this discussion con @Niklas B.

risposta

48

C'è una gestione speciale nell'origine per sopprimere l'errore "nome dell'argomento duplicato". Il messaggio di errore compare solo in shadowing_lvar_gen all'interno parse.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; 
    /* ... */ 

e idUScore è defined in id.c come questo:

REGISTER_SYMID(idUScore, "_"); 

Vedrete una gestione speciale simile a 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 di t he ciclo for.

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

In Ruby 2.0, il test idUScore == v[i] in warn_unused_var è sostituita 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 _ si. Quindi 2.0 allenta un po 'le cose.

+1

Mi chiedo se la differenza di comportamento di 'lambda {| _, _ | _} .call (4, 2) 'tra 1.8 e 1.9 è solo un effetto collaterale involontario allora? Come in circostanze "normali" in cui il nome della variabile non può essere duplicato, l'ordine in cui vengono assegnati è irrilevante. –

+0

Wow, mi hai battuto. +1 –

+3

@AndrewMarshall: Sì, penso che il problema "4 vs 2" sia solo un artefatto di come 1.8 e 1.9 gestiscono lo stack. L'unica volta che sarebbe visibile è '| _, _, ... |' perché l'errore di duplicazione è stato soppresso. –

22

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

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

È inoltre possibile utilizzare come nomi di metodo:

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

Naturalmente, non è esattamente un nome leggibile, né passare qualsiasi informazione al lettore su ciò che la variabile si riferisce o cosa fa il metodo

IRB, in particolare, imposta _ al valore dell'ultima espressione:

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

Orbene in the source code, è sufficiente impostare _ all'ultimo valore:

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

Ha esplorato qualche repository.Ecco cosa ho trovato:

Nelle ultime righe del file id.c, c'è la chiamata:

REGISTER_SYMID(idUScore, "_"); 

grep ing la fonte per idUScore mi ha dato due risultati apparentemente rilevanti:

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 grep ing la fonte per shadowing_lvar_gen, ho trovato il seguente on the changelog for Ruby 1.9.3:

Mar 11 Dic 2007 01:21:21 Yukihiro Matsumoto

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

che rischia di essere l'origine di this line:

if (idUScore == name) return name; 

Da questo, deduco che in una situazione come proc { |_, _| :x }.call :a, :b, uno _ variabile ombre semplicemente l'altra.


Ecco the commit in question. E 'fondamentalmente ha introdotto queste due righe:

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

Da un momento in cui idUScore non esisteva nemmeno, a quanto pare.

+5

Questo non spiega * affatto * perché 'lambda {| _, _ | 42} 'funziona mentre' lambda {| x, x | 42} 'no. –

+0

@AndrewMarshall, sembra che tu abbia ragione. '| _, _ |' funziona, ma '| __, __ |' no. '_' sembra avere un significato speciale, vedrò se riesco a scavare qualsiasi informazione dalla fonte Ruby. –

+1

A proposito, il tuo aggiornamento, sebbene informativo, non è rilevante in quanto si applica solo a IRb, che ho specificato nella mia domanda di cui non stavo parlando. –

Problemi correlati