2013-05-15 26 views
7

Se ho:Ambito di una variabile locale in un blocco

2.times do 
    i ||= 1 
    print "#{i} " 
    i += 1 
    print "#{i} " 
end 

ottengo 1 2 1 2, mentre io aspettavo 1 2 2 3. Perché i perde il suo incarico quando ricomincia il ciclo? Si comporta come previsto se l'assegnazione avviene al di fuori del ciclo, quindi suppongo che abbia a che fare con lo scope, ma non ho realizzato che i loop abbiano i loro scopi. Qualcuno può chiarire?

Aggiornamento: Grazie per l'aiuto su questo. Parte della mia confusione derivava dal venire a Ruby da Python, che non ha scope di blocco (credo).

+0

è questo per scopi pedagogici? perché questo tipo di codice è completamente unidiomatico ... – tokland

risposta

9

Osservare il codice qui sotto:

2.times do 
    p defined? i 
    i ||= 1 
    p defined? i 
    p "#{i} " 
    i += 1 
    p "#{i} " 
end 

uscita:

nil 
"local-variable" 
"1 " 
"2 " 
nil 
"local-variable" 
"1 " 
"2 " 

Ciò significa in ogni iterazione viene creato un nuovo ambito, e i è noto solo a tale ambito; che è dimostrato da nil e da "local-variable".

Ora i è creato al di fuori di block, e vedere l'uscita (senza nil viene):

i = nil 
2.times do 
    p defined? i 
    i ||= 1 
    p defined? i 
    p "#{i} " 
    i += 1 
    p "#{i} " 
end 

uscita:

"local-variable" 
"local-variable" 
"1 " 
"2 " 
"local-variable" 
"local-variable" 
"2 " 
"3 " 

sapere di più su ||= guardare What Ruby’s ||= (Double Pipe/Or Equals) Really Does

+0

Questo lo risolve bene, grazie! – ivan

3

Non è il "loop" che ha un ambito. È il blocco. Sì, un blocco è un ambito locale.

Se non si desidera che una variabile sia compresa come locale nel blocco, è necessario che esista all'esterno del blocco. Anche l'impostazione di i su zero in una riga precedente lo farebbe.

(Ma la tua aspettativa di 1 2 3 4 non sarà ancora abbastanza essere soddisfatto ...!)

+0

In realtà, anche solo "se false allora i = nil end" sarebbe sufficiente, ma esattamente * perché * è così probabilmente sarebbe troppo confuso spiegare in questo contesto. –

10

Non so quali sono le vostre aspettative siano basate su. Se pensi ciò che penso pensi, dovrebbe essere 1 2 2 3. È possibile ottenere ciò dichiarando la variabile i al di fuori del blocco.

i = nil 

2.times do 
    i ||= 1 
    print "#{i} " 
    i += 1 
    print "#{i} " 
end 

Quindi il blocco si chiude su quella variabile (chiusura) e lo utilizza. Senza chiusura, i è locale al blocco ed è nuovo ogni volta.

+0

hai ragione, ho confuso le mie aspettative :) – ivan

2

Puoi divertirti un po '. Supponiamo ad esempio di voler accedere all'ambito all'interno del blocco.

block = -> do 
    x = "Hello from inside a block" 
    binding # return the binding 
end 

p defined? x    #=> nil 
x = eval "x", block.call #=> #<Binding:0x007fce799c7dc8> 
p defined? x    #=> "local-variable" 
p x      #=> "Hello from inside a block" 

Questo è importante perché permette agli sviluppatori di soffiare via sostanzialmente l'incapsulamento di un blocco, e deve essere usato con cautela.

1

Risposta semplice è che si ripristina la variabile i su ogni iterazione e si reimposta sul valore di uno.

0

Il ciclo di rubino come x.times crea un ambito locale in modo che le variabili introdotte all'interno di questo ciclo siano locali e verranno distrutte dopo aver raggiunto la fine del blocco. Ecco perché non ottieni il risultato atteso. In ruby ​​se si desidera il ciclo senza ambito locale è possibile utilizzare il ciclo for o while che non crea l'ambito locale ma la variabile locale i rimarrà accessibile dopo il ciclo.

for j in (1..2) 
    i ||= 1 
    print "#{i} " 
    i += 1 
    print "#{i} " 
end 

stampa 1 2 2 3 come risultato previsto. Ora se eseguiamo il ciclo sopra inverso, il risultato sarà 3 4 4 5. Per informazioni supplementari e whilefor lavoro stesso (portata inferiore & stampa 1 2 2 3) d'altra parte each, x.upto(y) e x.times lavoro come stessi (creare ambito locale e stampare 1 2 1 2)

Problemi correlati