2011-02-06 19 views
33

Qual è la migliore spiegazione per i blocchi Ruby che è possibile condividere?La migliore spiegazione dei blocchi Ruby?

Sia l'utilizzo che la scrittura del codice possono richiedere un blocco?

+1

Stai cercando un'introduzione al concetto di blocco o un riferimento esauriente su di essi? – Phrogz

+16

O stai semplicemente trollando per un rappresentante ponendo domande a cui non hai bisogno di risposte, non intendi accettare e non intendi nemmeno partecipare alla discussione di? Vedremo se risponderai. – Phrogz

+0

Questa è una discussione utile: http://www.reactive.io/tips/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/ – Lucio

risposta

6

Il libro "Programming Ruby" ha un ottimo explanation of blocks and using them.

In 1.9+, l'elenco dei parametri passati in un blocco è diventato più sofisticato, che consente le variabili locali da definire:

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

;c,d dichiarare due nuove variabili locali all'interno del blocco, che non ricevono valori dal l'istruzione 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 la "splat" operatore *, che lavora nella lista dei parametri:

do |a,*b| 
    some_stuff 
end 

assegnerebbe il primo dei valori multipli ad "a", e tutto il resto sarebbe stato catturato in "b" che verrebbe trattato come un array. Il * potrebbe essere sulla variabile a:

do |*a,b| 
    some_stuff 
end 

sarebbe catturare tutti passati nelle variabili, tranne l'ultimo, che sarebbe passato a b. E, analogamente ai due precedenti:

do |a,*b,c| 
    some_stuff 
end 

sarebbe assegnare il primo valore di a, l'ultimo valore di c e tutte/tutti i valori intermedi a b.

Penso che sia piuttosto potente e lucido.

Ad 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 
19

Da Why's (poignant) guide to ruby:

Qualsiasi codice circondato da parentesi graffe è un blocco.

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

Con i blocchi, è possibile raggruppare insieme una serie di istruzioni in modo che possano passare intorno al programma. Le parentesi graffe danno l'aspetto delle pinze di granchio che hanno strappato il codice e lo tengono insieme. Quando vedi vedi queste due pinze, ricorda che il codice all'interno è stato premuto in una singola unità.E 'come uno di quelle piccole scatole di Ciao Kitty che vendono al centro commerciale che è farcito con piccole matite e carta microscopica, tutti stipati in un caso trasparente glitterato che può essere nascosto nel palmo della mano per segrete stazionarie operazioni. Tranne che i blocchi non richiedono così tanti strabismo. Le parentesi graffe ricche possono anche essere scambiate per le parole e fine, 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 

argomenti blocchi sono un insieme di variabili circondato da tubo caratteri, separati da virgole .

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

argomenti blocchi sono utilizzati all'inizio di un blocco.

{ |x,y| x + y } 

Nell'esempio precedente, | x, y | sono gli argomenti. Dopo gli argomenti, abbiamo un po 'di codice. L'espressione x + y aggiunge i due argomenti insieme. I mi piace pensare ai caratteri del tubo come a rappresentare un tunnel. Danno a l'aspetto di uno scivolo che le variabili stanno scivolando verso il basso. (Una x va scendendo aquila, mentre l'ordinatamente incrocia le gambe.) Questo scivolo funge da un passaggio tra i blocchi e il mondo intorno a loro. Le variabili sono passate attraverso questo scivolo (o tunnel) nel blocco.

+16

"Qualsiasi codice circondato da parentesi graffe è un blocco" a meno che è un ** hash **. – Meltemi

+1

Non si spiega cosa restituiscono questi esempi. Non capisco –

+0

Si prega di essere il mio tutor! Grazie per averlo spiegato in modo così semplice e chiaro. – Benjamints

3

I blocchi sono un modo di 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.times do 
    puts 'hi' 
end 

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

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

blocchi (in Ruby 1.9.2) può avere esplicitamente variabili locali:

 
x = 'hello' 
2.times do |;x| 
    x = 'world' 
    puts x 
end 

=> world 
=> world 

variabili locali possono essere combinati con i parametri:

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

Tutte le funzioni possono ricevere un argomento di blocco predefinito:

 
def twice 
    yield 
    yield 
end 

twice { puts 'hello' } 
=> hello 
=> hello 

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

 
array = [1,2] 

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

puts array.map do |n| n*10 end # (puts array.map) do |n| n*10 end 
=> <Enumerator:0x00000100862670> 
2

blocchi sono letterali leggeri per le procedure di prima classe anonima con alcune limitazioni fastidiose. Essi funzionano allo stesso modo in Ruby mentre lavorano in quasi ogni altro linguaggio di programmazione, modulo i limiti già citati, che sono:

  • blocchi possono essere visualizzati solo in liste di argomenti
  • al massimo un blocco può apparire

    in una lista di argomenti (e deve essere l'ultimo argomento)
+0

Buona risposta ma la relazione con gli oggetti Proc sembra essenziale, no? – maerics

+0

@maerics Essenziale per una risorsa esaustiva su Blocks? Sì. Essenziale per una spiegazione dei blocchi (che io interpreto come introduzione a loro per i novizi)? Sicuramente no, IMO. – Phrogz

+0

Grazie. La tua è l'unica risposta che mi ha aiutato a capire perché '{puts" ciao "}' non funziona. Non è permesso affatto? Quello è strano. –

27

offro la mia personale spiegazione da this answer, leggermente modificata:

"Blocchi" in Ruby non sono gli stessi dei termini generali di programmazione " blocco di codice "o r "blocco di codice".

finta per un momento che la seguente (non valido) codice Ruby effettivamente lavorate:

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 aggirando codice per un metodo ed avente tale metodo eseguito il codice è possibile in Ruby in una varietà di 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| ... } o foo do |x,y,z| ... end, questi sono blocchi che prendono tre parametri ed eseguono il ... su di essi. (si potrebbe anche vedere che il metodo di cui sopra upto sta passando un blocco.)

Perché blocchi sono una parte speciale della sintassi Ruby, ogni metodo è permesso di essere passato un blocco. Indipendentemente dal fatto che il metodo utilizza, il blocco è valido per il metodo. Per esempio:

def say_hi(name) 
    puts "Hi, #{name}!" 
end 

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

Il metodo di cui sopra viene fatto passare un blocco che è pronta a rilasciare un insulto, ma dal momento che il metodo non chiama mai il blocco, solo il bel messaggio viene stampato. Ecco come noi 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 è stato passato insieme o no. 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? 

E 'solo una convenzione (e una buona, e quello che si desidera supportare) per alcune classi di passare l'istanza appena creato al blocco.

Questa non è una risposta esauriente, in quanto non copre blocchi cattura come argomenti, come gestire arity, non-splatting nei parametri di blocco, ecc, ma si propone di fungere da intro Blocchi-Are-lambda.

26

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

  1. costruzione come oggetto Proc (o usando la parola chiave lambda):

    add1 = Proc.new {|x| x+1} # Returns its argument plus one. 
    add1.call(1) # => 2 
    
  2. Superato come metodo pseudo argomento, utilizzando esplicitamente l'operatore dello zucchero di sintassi dell'ultimo argomento speciale & o implicitamente utilizzando una coppia block_given?/yield:

    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 Visitor patterns; i dati possono essere passati agli argomenti di blocco speciali come argomenti ai metodi call o yield.

+4

Le parentesi graffe hanno un'alta precedenza; 'do' ha una precedenza bassa. Se il richiamo del metodo ha parametri che non sono racchiusi tra parentesi, il modulo controvento di un blocco si collegherà all'ultimo parametro, non alla chiamata generale. Il modulo 'do' si collegherà all'invocazione. – Green

+0

Perché il downvote? – maerics

+2

inglese, per favore! ...... "I blocchi ruby ​​sono letterali di sintassi per oggetti Proc ...." - se le persone non sanno cos'è un blocco, suppongo che non sapranno cosa "sintassi letterali per oggetti Proc" significa . prova a spiegare come se i lettori avessero 5 anni. – BKSpurgeon

6

Per chiunque a venire a questa domanda da un background C# (o altri Langs realmente), questo potrebbe aiutare:

blocchi di Ruby sono come le 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.

Rubino: { |x| x + 1 }

C#: x => x + 1

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

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

Sia C# e Ruby offrire modi alternativi di scrivere l'esempio di cui sopra.

Rubino:

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

C#:

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

Sia in Ruby e C#, più istruzioni sono ammessi, In Ruby, la seconda sintassi di cui sopra è richiesto per questo.

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

Problemi correlati