2012-01-17 8 views
7

L'istruzione break per i blocchi (come da The Ruby Programming Language) è definito come segue:Perché l'istruzione break in ruby ​​si comporta diversamente quando si utilizza Proc.new v. Il segno e commerciale?

provoca il blocco di ritornare alla sua iteratore e l'iteratore tornare al metodo invocato.

Pertanto, quando viene eseguito il codice seguente, risulta un LocalJumpError.

def test 
    puts "entering test method" 
    proc = Proc.new { puts "entering proc"; break } 
    proc.call # LocalJumpError: iterator has already returned 
    puts "exiting test method" 
end 
test 

Mentre il seguente codice non lo fa gettare un LocalJumpError. Qual è la particolarità del segno e commerciale? Il segno e commerciale non usa implicitamente Proc.new?

def iterator(&proc) 
    puts "entering iterator" 
    proc.call # invoke the proc 
    puts "exiting iterator" # Never executed if the proc breaks 
end 

def test 
    iterator { puts "entering proc"; break } 
end 
test 

In altre parole, ho letto il cartello commerciale come mezzo di in-rivestimento chiamata Proc.new. A quel punto il comportamento dovrebbe essere lo stesso del primo snippet di codice.

def iterator (p = Proc.new { puts "entering proc"; break}) 
... 
end 

responsabilità: Sto newb imparare la lingua (ruby 1.9.2), e quindi apprezzerò riferimenti e una sinossi dettagliata.

+1

Non c'è tempo per una risposta corretta, ma questo riguarda lo scope, non tanto un Proc o un Lambda che sono speciali. – coreyward

+0

quando si ottiene il tempo ... si prega di visitare di nuovo questa domanda. Apprezzerei la tua intuizione –

+0

Invece di 'Proc.new' prova' lambda'. – Casper

risposta

6

break rende il blocco e il chiamante del ritorno blocco. Nel codice seguente:

proc = Proc.new { break } 

Il "chiamante" del blocco che viene convertito in un oggetto Proc è Proc.new. break si suppone che renda il chiamante del blocco restituito, ma Proc.new è già stato restituito.

In questo codice:

def iterator(&b); b.call; end 
iterator { break } 

Il chiamante del blocco è iterator, in modo che rende iterator ritorno.

+0

capito ... questa è la migliore risposta. Tuttavia, non tutti i blocchi vengono convertiti in codice utilizzabile tramite Proc.new? Quindi l'iteratore (& b) non diventa iteratore (b = Proc.new b)? –

+0

e Proc.new non sono la stessa cosa. & è la sintassi principale; Proc.new è un metodo di libreria. Puoi scrivere il tuo Proc.new in questo modo: 'class Proc; def self.new (&b); b; end; end'. Ma non è possibile implementare la propria sintassi di base (oltre a hackerare l'interprete o utilizzare un preprocessore). –

3

Ecco lo answer.

E commerciale è utilizzato per convertire un proc in un blocco e un blocco in un proc.

ho cambiato l'esempio in modo da mettere in relazione al vostro caso:

def run_my_code(&my_code) 
puts 'before proc' 
my_code.call 
puts 'after proc' 
end 
run_my_code { puts "passing a block, accepting a proc"; break} 
=> before proc 
    passing a block, accepting a proc 

Come si può vedere che non ha raggiunto il 'dopo proc'

def run_my_code 
yield 
end 
my_proc = Proc.new { puts "passing a proc instead of block"; break} 
run_my_code &my_proc 
=> passing a proc instead of block 
    LocalJumpError: break from proc-closure 
    from (pry):75:in `block in <main>' 

Nel suo secondo esempio si dispone un proc in risultato, il proc si interrompe da iterator e torna alla funzione test.

def iterator(&proc) 
    puts 'entering iterator' 
    proc.call 
    puts 'exiting iterator' 
end 

def test 
    puts 'before test' 
    iterator { puts 'entering proc'; break } 
    puts 'after test' 
end 

=>before test 
entering iterator 
entering proc 
after test 
+0

Se ho questo corretto, nel primo esempio l'istruzione break ritorna da Proc.new (come quello è un iteratore) e dal blocco che lo racchiude. Ho documentato la definizione all'inizio della domanda come ho capito, perché il primo ha fallito. Ma, se nel secondo esempio il segno e commerciale è zucchero sintattico per Proc.new, quindi non capisco, perché non fallisce lì –

+0

Ho risolto la risposta. – megas

+0

quindi, se un blocco è stato convertito in un proc, allora l'istruzione break non dovrebbe funzionare come il primo snippet di codice? Voglio dire, se stiamo fondamentalmente inserendo un Proc.new, perché questo si comporta diversamente da un Proc.new che era in una riga separata sopra la chiamata al metodo? –

0

ha a che fare con la differenza tra blocchi, procs e lambda - ei rispettivi ambiti.

ho scritto un post su di esso già nel 2009 che potreste trovare utili: http://www.leonardoborges.com/writings/2009/07/22/procs-lambdas-blocks-whats-the-difference/

Spero che questo aiuti.

+0

Il post era chiaramente perspicace, ma dato che sto usando un proc in entrambi i casi, non sono sicuro del motivo per cui Ruby sta cercando di dare un trattamento speciale a un proc rispetto ad un altro? –

+0

Ha a che fare con la spiegazione della parola chiave di ritorno nel post. break, come return in questo caso significa break dal metodo di chiamata, test in questo caso. Tuttavia non puoi interrompere il test poiché puoi verificare mettendo un'interruzione subito dopo 'Proc.new {...}' Che è diverso sul secondo snippet dal momento che tornerà dal metodo di chiamata, iterator in questo caso, restituendo il controllo alla nuova versione del test. – leonardoborges

+0

Quindi se il test è stato inserito in un metodo chiamato test_test, l'istruzione break in Proc.new non dovrebbe fallire? Qui' come sto leggendo il blocco alla conversione proc def iteratore (&proc); chiamata #implicity proc = Proc.new {} proc; ... end In questo caso, il LocalJumpError dovesse accadere, proprio come in prova. –

Problemi correlati