2016-01-09 7 views
8

Se faccio una partita con un espressione regolare con dieci cattura:Perché solo un numero limitato di acquisizioni di espressioni regolari è memorizzato in `global_variables`?

/(o)(t)(th)(f)(fi)(s)(se)(e)(n)(t)/.match("otthffisseent") 

poi, per $10, ottengo:

$10 # => "t" 

ma non è presente global_variables. Ottengo (in una sessione IRB):

[:$;, :$-F, :[email protected], :$!, :$SAFE, :$~, :$&, :$`, :$', :$+, :$=, :$KCODE, :$-K, :$,, 
:$/, :$-0, :$\, :$_, :$stdin, :$stdout, :$stderr, :$>, :$<, :$., :$FILENAME, 
:$-i, :$*, :$?, :$$, :$:, :$-I, :$LOAD_PATH, :$", :$LOADED_FEATURES, 
:$VERBOSE, :$-v, :$-w, :$-W, :$DEBUG, :$-d, :$0, :$PROGRAM_NAME, :$-p, :$-l, 
:$-a, :$binding, :$1, :$2, :$3, :$4, :$5, :$6, :$7, :$8, :$9] 

Qui, solo i primi nove sono elencati:

$1, :$2, :$3, :$4, :$5, :$6, :$7, :$8, :$9 

Ciò è confermato anche da:

global_variables.include?(:$10) # => false 

Dove è $10 memorizzato, e perché non è memorizzato in global_variables?

+1

Puoi usare '$ ~' che contiene un oggetto MatchData, attraverso il quale puoi accedere a tutte le partite che iniziano con l'indice 1. –

+2

@TamerShlash puoi anche usare '$ 10', ma ciò non spiega dove si trova quella variabile globale memorizzato e perché manca da 'global_variables' – Stefan

+0

Esatto, ho letto male la domanda. –

risposta

6

Le variabili numerate restituite da Kernel#global_variables saranno sempre le stesse, anche prima che vengano assegnate. Cioè $1 tramite $9 verrà restituito prima ancora che si esegua la corrispondenza e le altre corrispondenze non verranno aggiunte all'elenco. (Possono anche non essere assegnati, per esempio usando $10 = "foo".)

Si consideri il codice sorgente per il metodo:

VALUE 
rb_f_global_variables(void) 
{ 
    VALUE ary = rb_ary_new(); 
    char buf[2]; 
    int i; 

    st_foreach_safe(rb_global_tbl, gvar_i, ary); 
    buf[0] = '$'; 

    for (i = 1; i <= 9; ++i) { 
     buf[1] = (char)(i + '0'); 
     rb_ary_push(ary, ID2SYM(rb_intern2(buf, 2))); 
    } 

    return ary; 
} 

È possibile (dopo abituarsi a guardare C) vedere dal ciclo for che la i simboli $1 tramite $9 sono codificati nel valore restituito del metodo.

Quindi, come è possibile utilizzare ancora $10 se l'output di global_variables non cambia? Bene, l'output potrebbe essere un po 'fuorviante, perché suggerirebbe che i dati della partita fossero memorizzati in variabili separate, ma questi sono solo scorciatoie, delegando l'oggetto MatchData memorizzato in $~.

In sostanza $n visualizza $~[n]. Questo oggetto MatchData (proveniente dalla tabella globale) fa parte dell'output originale del metodo, ma non viene assegnato fino a quando non si esegue una corrispondenza.

Per quanto riguarda la giustificazione per l'inclusione di $1 tramite $9 nell'output della funzione, è necessario chiedere a qualcuno del team principale di Ruby. Potrebbe sembrare arbitrario, ma è probabile che sia stata presa una decisione in merito.

+0

* "I valori restituiti da Kernel # global_variables saranno sempre gli stessi" * - Questo non è vero. Se faccio '$ foo = 1', quindi' $ pippo' sarà aggiunto a 'global_variables'. – sawa

+0

@sawa: buon punto. Fisso. – Drenmi

+1

Ancora un mistero: come viene valutato $ 10 se non è una variabile globale –

9

Rubino sembra trattare $1, $2 ecc a livello di parser:

ruby --dump parsetree_with_comment -e '$100' 

uscita:

########################################################### 
## Do NOT use this node dump for any purpose other than ## 
## debug and research. Compatibility is not guaranteed. ## 
########################################################### 

# @ NODE_SCOPE (line: 1) 
# | # new scope 
# | # format: [nd_tbl]: local table, [nd_args]: arguments, [nd_body]: body 
# +- nd_tbl (local table): (empty) 
# +- nd_args (arguments): 
# | (null node) 
# +- nd_body (body): 
#  @ NODE_NTH_REF (line: 1) 
#  | # nth special variable reference 
#  | # format: $[nd_nth] 
#  | # example: $1, $2, .. 
#  +- nd_nth (variable): $100 

BTW, il maximum number of capture groups è 32.767 e si può accedere a tutti via $n:

/#{'()' * 32768}/  #=> RegexpError: too many capture groups are specified 

/#{'()' * 32767}/ =~ '' #=> 0 
defined? $32767   #=> "global-variable" 
$32767     #=> "" 
+1

Bello! Questo è il complemento di cui necessitava la risposta. :-) – Drenmi

+2

Incredibile potere di Ruby. Non resterò mai senza gruppi di cattura. – sawa

+3

[PCRE2 supporta 65.535 gruppi di acquisizione] (http://stackoverflow.com/a/33928343/3832970), quindi batte Ruby :) Tuttavia, dubito che ne userai mai più di 99, nella vita reale, preferiremmo ottenere più corrispondenze che provare a catturare tutti i dettagli con una regex ingombrante. –

3

consideriamo t il suo comportamento come un bug. Lo abbiamo sistemato nel bagagliaio.

+1

Sei uno sviluppatore del progetto in questione? Potresti dire qualcosa a riguardo, o in qualche modo approfondire cosa intendi. Forse quale versione si aggiusterà o ha risolto? – melwil

+0

@melwil, lols. sì, potrebbe aver creato la lingua ... – Intentss

Problemi correlati