La migliore spiegazione dei blocchi di Ruby?

Qual è la migliore spiegazione per i blocchi di Ruby che puoi condividere?

Sia l’uso che la scrittura del codice possono prendere un blocco?

Offro la mia spiegazione da questa risposta , leggermente modificata:

I “blocchi” in Ruby non corrispondono ai termini generali di programmazione “blocco di codice” o “blocco di codice”.

Fai finta che il seguente codice Ruby (non valido) abbia funzionato:

def add10( n ) puts "#{n} + 10 = #{n+10}" end def do_something_with_digits( method ) 1.upto(9) do |i| method(i) end end do_something_with_digits( add10 ) #=> "1 + 10 = 11" #=> "2 + 10 = 12" ... #=> "9 + 10 = 19" 

Mentre quel codice non è valido, il suo intento, il passaggio di un codice ad un metodo e l’esecuzione di tale metodo, è ansible in Ruby in vari modi. Uno di questi modi è “Blocchi”.

Un blocco in Ruby è molto, molto simile a un metodo: può richiedere alcuni argomenti ed eseguire il codice per quelli. Ogni volta che vedi foo{ |x,y,z| ... } foo{ |x,y,z| ... } o foo do |x,y,z| ... end foo do |x,y,z| ... end , quelli sono blocchi che prendono tre parametri ed eseguono il ... su di loro. (Potresti anche vedere che il metodo sopra riportato viene passato a un blocco.)

Poiché i Blocchi sono una parte speciale della syntax Ruby, a ogni metodo è consentito passare un blocco. Indica se il metodo utilizza il blocco o meno fino al metodo. Per esempio:

 def say_hi( name ) puts "Hi, #{name}!" end say_hi("Mom") do puts "YOU SUCK!" end #=> Hi, Mom! 

Il metodo sopra è passato un blocco che è pronto per emettere un insulto, ma poiché il metodo non chiama mai il blocco, viene stampato solo il messaggio carino. Ecco come chiamiamo il blocco da un metodo:

 def say_hi( name ) puts "Hi, #{name}!" if block_given? yield( name ) end end say_hi("Mridang") do |str| puts "Your name has #{str.length} letters." end #=> Hi, Mridang! #=> Your name has 7 letters. 

Usiamo block_given? per vedere se un blocco è passato o meno. In questo caso abbiamo passato un argomento indietro al blocco; spetta al tuo metodo decidere cosa passare al blocco. Per esempio:

 def say_hi( name ) puts "Hi, #{name}!" yield( name, name.reverse ) if block_given? end say_hi("Mridang"){ |str1, str2| puts "Is your name #{str1} or #{str2}?" } #=> Hi, Mridang! #=> Is your name Mridang or gnadirM? 

È solo una convenzione (e una buona, e una che si desidera supportare) per alcune classi per passare l’istanza appena creata nel blocco.

Questa non è una risposta esaustiva, in quanto non copre i blocchi di cattura come argomenti, come gestiscono l’arità, non splattano nei parametri di blocco, ecc. Ma intende essere intro come l’introduzione di Blocks-Are-Lambdas.

I blocchi ruby ​​sono un modo per creare oggetti Proc che rappresentano il codice che può essere utilizzato da un altro codice. Gli oggetti Proc sono istruzioni tra parentesi graffe {} (o do...end frasi do...end per blocchi multilinea, che hanno precedenza inferiore rispetto alle parentesi graffe) che possono facoltativamente accettare argomenti e restituire valori (ad esempio {|x,y| x+y} ) . I Proc sono oggetti di prima class e possono essere costruiti esplicitamente o raggiunti implicitamente come pseudo-argomenti del metodo:

  1. Costruzione come object Proc (o usando la parola chiave lambda ):

     add1 = Proc.new {|x| x+1} # Returns its argument plus one. add1.call(1) # => 2 
  2. Passato come pseudo argomento del metodo, usando esplicitamente l’operatore dello zucchero di syntax speciale & dell’ultimo argomento o usando implicitamente un block_given? / yield pair:

     def twice_do(&proc) # "proc" is the block given to a call of this method. 2.times { proc.call() } if proc end twice_do { puts "OK" } # Prints "OK" twice on separate lines. def thrice_do() # if a block is given it can be called with "yield". 3.times { yield } if block_given? end thrice_do { puts "OK" } # Prints "OK" thrice on separate lines. 

Il secondo modulo viene in genere utilizzato per i pattern Visitor ; i dati possono essere passati agli argomenti del blocco speciale come argomenti ai metodi call o yield .

Dalla guida di Why (struggente) al ruby :

Qualsiasi codice circondato da parentesi graffe è un blocco.

2.times { print "Yes, I've used chunky bacon in my examples, but never again!" } 2.times { print "Yes, I've used chunky bacon in my examples, but never again!" } è un esempio.

Con i blocchi, puoi raggruppare insieme una serie di istruzioni in modo che possano essere passate attorno al tuo programma. Le parentesi graffe danno l’aspetto di chele di granchio che hanno strappato il codice e lo tengono insieme. Quando vedi queste due tenaglie, ricorda che il codice all’interno è stato premuto in una singola unità. È come una di quelle piccole scatole di Hello Kitty che vendono al centro commerciale piene di piccole matite e carta microscopica, il tutto racchiuso in una custodia scintillante trasparente che può essere nascosta nel palmo della mano per operazioni segrete nascoste. Tranne che i blocchi non richiedono così tanti strabismo. Le parentesi graffe possono anche essere scambiate per le parole do e end, il che è bello se il blocco è più lungo di una riga.

 loop do print "Much better." print "Ah. More space!" print "My back was killin' me in those crab pincers." end 

Gli argomenti di blocco sono un insieme di variabili circondate da caratteri di pipe e separate da virgole.

 |x|, |x,y|, and |up, down, all_around| are examples. 

Gli argomenti di blocco vengono utilizzati all’inizio di un blocco.

 { |x,y| x + y } 

Nell’esempio sopra, | x, y | sono gli argomenti. Dopo gli argomenti, abbiamo un po ‘di codice. L’espressione x + y aggiunge i due argomenti insieme. Mi piace pensare ai personaggi della pipa come a un tunnel. Danno l’aspetto di uno scivolo che le variabili stanno scivolando verso il basso. (Una x scende verso l’aquila, mentre le attraversa ordinatamente le gambe). Questo scivolo funge da passaggio tra i blocchi e il mondo che li circonda. Le variabili vengono passate attraverso questo scivolo (o tunnel) nel blocco.

Il libro ” Programming Ruby ” ha una grande spiegazione dei blocchi e il loro utilizzo .

In 1.9+, l’elenco dei parametri passati in un blocco è diventato più sofisticato, consentendo di definire le variabili locali:

 do |a,b;c,d| some_stuff end 

;c,d dichiara due nuove variabili locali all’interno del blocco, che non ricevono valori dall’istruzione di yield della routine chiamata. Ruby 1.9+ garantisce che, se le variabili esistessero al di fuori del blocco, non verranno calpestate dalle variabili con lo stesso nome all’interno del blocco. Questo è un nuovo comportamento; 1.8 li calpesterebbe.

 def blah yield 1,2,3,4 end c = 'foo' d = 'bar' blah { |a, *b; c,d| c = 'hello' d = 'world' puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}", "d: #{d}" } puts c, d # >> a: 1 # >> b: 2,3,4 # >> c: hello # >> d: world # >> foo # >> bar 

C’è anche l’operatore “splat” * , che funziona nell’elenco dei parametri:

 do |a,*b| some_stuff end 

Assegnerei il primo di più valori a “a”, e tutto il resto sarebbe catturato in “b” che sarebbe trattato come un array. Il * potrebbe essere sulla variabile a:

 do |*a,b| some_stuff end 

catturerebbe tutto passato in variabili eccetto l’ultimo, che sarebbe passato a b . E, in modo simile ai due precedenti:

 do |a,*b,c| some_stuff end 

assegnerebbe il primo valore a a , l’ultimo valore a c e tutti / tutti i valori intermedi a b .

Penso che sia piuttosto potente e shiny.

Per esempio:

 def blah yield 1,2,3,4 end blah { |a, *b| puts "a: #{a}", "b: #{b.join(',')}" } # >> a: 1 # >> b: 2,3,4 blah { |*a, b| puts "a: #{a.join(',')}", "b: #{b}" } # >> a: 1,2,3 # >> b: 4 blah { |a, *b, c| puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}" } # >> a: 1 # >> b: 2,3 # >> c: 4 

Per chiunque venga a questa domanda da uno sfondo C # (o altri lang in realtà), questo potrebbe aiutare:

I blocchi ruby ​​sono come espressioni lambda e metodi anonimi in C #. Sono ciò che C # chiama delegati (e Ruby chiama Procs), vale a dire che sono essenzialmente funzioni che possono essere passate come valori. In entrambi Ruby e C #, possono anche comportarsi come chiusure.

Ruby: { |x| x + 1 } { |x| x + 1 }

C #: x => x + 1

Ruby: { |name| puts "Hello there #{name}" } { |name| puts "Hello there #{name}" }

C #: name => { Console.WriteLine("Hello there {0}", name); } name => { Console.WriteLine("Hello there {0}", name); }

Sia C # che Ruby offrono metodi alternativi per scrivere l’esempio precedente.

Rubino:

 do |name| puts "Hello there #{name}" end 

C #:

 delegate(string name) { Console.WriteLine("Hello there {0}", name); } 

Sia in Ruby che in C # sono consentite più istruzioni, In Ruby, per questo è necessaria la seconda syntax sopra.

Questi concetti sono disponibili in molte altre lingue che sono state influenzate dalle idee alla base della programmazione funzionale.

I blocchi sono un modo per raggruppare il codice in Ruby. Ci sono due modi per scrivere blocchi. Uno sta usando l’istruzione do..end e l’altro sta circondando il codice tra parentesi graffe: {}. I blocchi sono considerati oggetti nel linguaggio di programmazione Ruby e, per impostazione predefinita, tutte le funzioni accettano un argomento di blocco implicito.

Ecco due esempi di blocchi che fanno la stessa cosa:

 2.times {puts 'hi'}
 2 volte fare
   mette 'ciao'
 fine

I blocchi possono ricevere liste di argomenti separati da virgole all’interno di barre verticali ||. Per esempio:

 [1,2] .map {| n |  n + 2} # [3, 4]

I blocchi (in ruby ​​1.9.2) possono avere esplicitamente variabili locali:

 x = 'ciao'
 2.times do |; x |
   x = 'mondo'
   mette x
 fine

 => mondo
 => mondo

Le variabili locali possono essere combinate con parametri:

 [1,2] .map {| n; x |  n + 2}

Tutte le funzioni possono ricevere un argomento di blocco predefinito:

 Def due volte
   dare la precedenza
   dare la precedenza
 fine

 due volte {puts 'hello'}
 => ciao
 => ciao

Qual è la differenza tra i blocchi do..end e {}? Per convenzione {} i blocchi sono su una singola riga e i blocchi do..end si estendono su più righe, poiché sono entrambi più facili da leggere in questo modo. La principale differenza ha a che fare con la precedenza:

 array = [1,2]

 mette array.map {| n |  n * 10} # puts (array.map {| n | n * 10})
 => 10
 => 20

 mette array.map fa | n |  n * 10 end # (puts array.map) do | n |  n * 10 fine
 => 

I blocchi sono letterali leggeri per procedure anonime di prima class con alcune fastidiose limitazioni. Funzionano allo stesso modo in Ruby poiché funzionano in quasi tutti gli altri linguaggi di programmazione, modulo le limitazioni sopra menzionate, che sono:

  • i blocchi possono apparire solo negli elenchi di argomenti
  • al massimo un blocco può apparire in una lista di argomenti (e deve essere l’ultimo argomento)