2009-05-02 16 views
47

Visto questo pezzo di codice in un libro Ruby on Rails. Questo primo è da una vista e il secondo è un modulo helper. Non capisco come funzionano le cose &block e . Qualcuno può guidarmi ad un tutorial di qualche tipo che lo spieghi?Cos'è questo e blocco in Ruby? E come viene passato in un metodo qui?

<% hidden_div_if(@cart.items.empty?, :id => "cart") do %> 
<%= render(:partial => "cart", :object => @cart) %> 
<% end %> 

module StoreHelper 
def hidden_div_if(condition, attributes = {}, &block) 
    if condition 
    attributes["style"] = "display: none" 
    end 
    content_tag("div", attributes, &block) 
    end 
end 

risposta

78

I blocchi sono una parte piuttosto fondamentale del rubino. Sono delimitati da do |arg0,arg1| ... end o { |arg0,arg1,arg2| ... }.

Essi consentono di specificare un callback da passare a un metodo. Questo richiamata può essere invocato in due modi - sia catturando specificando un argomento finale prefissato con &, oppure utilizzando la parola chiave yield:

irb> def meth_captures(arg, &block) 
     block.call(arg, 0) + block.call(arg.reverse , 1) 
    end 
#=> nil 
irb> meth_captures('pony') do |word, num| 
     puts "in callback! word = #{word.inspect}, num = #{num.inspect}" 
     word + num.to_s 
    end 
in callback! word = "pony" num = 0 
in callback! word = "ynop" num = 1 
#=> "pony0ynop1" 
irb> def meth_yields(arg) 
     yield(arg, 0) + yield(arg.upcase, 1) 
    end 
#=> nil 
irb> meth_yields('frog') do |word, num| 
     puts "in callback! word = #{word.inspect}, num = #{num.inspect}" 
     word + num.to_s 
    end 
in callback! word = "frog", num = 0 
in callback! word = "FROG", num = 1 
#=> "frog0FROG1" 

Nota che la nostra richiamata è stata la stessa in ogni caso - abbiamo possibile rimuovere la ripetizione di salvando la nostra callback in un oggetto e quindi passandola a ciascun metodo . Ciò può essere fatto utilizzando lambda per acquisire la richiamata in un oggetto, e quindi passata a un metodo aggiungendo il prefisso a &.

irb> callback = lambda do |word, num| 
     puts "in callback! word = #{word.inspect}, num = #{num.inspect}" 
     word + num.to_s 
    end 
#=> #<Proc:[email protected](irb):22> 
irb> meth_captures('unicorn', &callback) 
in callback! word = "unicorn", num = 0 
in callback! word = "nrocinu", num = 1 
#=> "unicorn0nrocinu1" 
irb> meth_yields('plate', &callback) 
in callback! word = "plate", num = 0 
in callback! word = "PLATE", num = 1 
#=> "plate0PLATE1" 

E 'importante capire i diversi usi della & qui come un prefisso per l'ultimo argomento di una funzione

  • in una definizione di funzione, che cattura qualsiasi blocco passato in quell'oggetto
  • in una chiamata di funzione, espande l'oggetto callback specificato in un blocco

Se si guardano intorno i blocchi vengono utilizzati in tutto il luogo, in particolare in iteratori, come Array#each.

+0

Grazie per questo, davvero utile per me! – duykhoa

+0

Grazie amico - ottimo esempio, ma che senso ha chiamare ispezionare su un Fixnum? – sekmo

10

Il &block è un modo di inviare un pezzo di codice Ruby in un metodo e poi valutare il codice nella portata di tale metodo. Nel tuo codice di esempio sopra significa che un carrello con nome parziale verrà reso in un div. Penso che il termine closure sia usato per questo in informatica.

Quindi nel tuo esempio, la &block è:

<%= render(:partial => "cart", :object => @cart) %> 

qualche buona lettura e una spiegazione dei blocchi, procs e lamdas può essere trovato alla Robert Sosinski's blog.

+1

Sì, i blocchi in Ruby sono solo chiusure. Per una buona spiegazione di ciò, vedi http://reprog.wordpress.com/2010/02/27/closures-finally-explained/. –

4

Re attributes = {}, questo è solo un argomento di metodo con un valore predefinito. Quindi, se chiami hidden_div_if(whatever), ovvero passando solo il primo argomento, il valore attributes verrà impostato come predefinito su un hash vuoto.

Questo è utile perché semplifica l'impostazione attributes["style"] più tardi, come attributes non deve essere inizializzato a un hash prima. (Che potrebbe comunque essere fatto semplicemente come (attributes ||= {})["style"] = ….)


&block è solo un po 'più complicato.

I metodi di Ruby possono prendere un ultimo argomento che è un blocco, utilizzando la sintassi speciale method(args) { |block_args| block_code }. &block acquisisce fondamentalmente tale blocco nella variabile block come oggetto Proc. Quindi block è solo una variabile che punta a una procedura anonima qui.

Quando più tardi content_tag si chiama, e &block viene passato come il suo ultimo argomento, si è espansa in un blocco, come se la chiamata era davvero content_tag(…) { block originally passed to hidden_if_div }


Così forse ero davvero confuso qui. Quello che dovresti cercare su google è "ruby default arguments" e "ruby blocks".

1

funziona così:

@cart.items.empty? è la codition

:id => "cart" Diventa attributi come da convenzione è possibile rimuovere {} su un hash param se è ultimo.

il blocco è

render(:partial => "cart", :object => @cart)

così all'interno della funzione, se il carrello è vuoto si aggiungerà l'attributo stile con valore "display: none"

Poi si creerà un tag div riempito con il contenuto del risultato dell'esecuzione del blocco che sarà il risultato del rendering del carrello della vista parziale con il contenuto di @cart.

3

Ruby implementa blocchi, processi e lambda che vengono definiti come chiusure nella comunità di informatica. Se inizi a imparare Ruby ti imbatterai velocemente in un codice simile a questo.

a = ["dog", "cat", "bird"] 
a.alter_each! do |n, i| 
    "#{i}_#{n}" 
end 

Quindi, che cosa sta succedendo qui?

Iniziamo con una serie di nomi di animali e chiamiamo alter_each! metodo che passa un blocco. In questo blocco di codice possiamo specificare come vogliamo modificare ciascun elemento. Il nostro esempio prefissa il nome di ogni animale con la sua posizione nell'array. Come l'alter_each! il metodo itera su ogni elemento che eseguirà il nostro blocco passando il valore e l'indice. Il nostro blocco cattura questi parametri, antepone l'indice al nome e restituisce il risultato. Ora guardiamo all'alter_each! metodo.

Si noti che il metodo non specifica alcun parametro, poiché un blocco viene assegnato automaticamente alla parola chiave yield. yield è chiamato come una funzione che passa nel valore e nell'indice di ogni elemento dell'array e sovrascrive il valore originale.

class Array 
    def alter_each! 
    self.each_with_index do |n, i| 
     self[i] = yield(n,i) 
    end 
    end 
end 

E se fosse necessario passare un parametro a questo metodo?

È possibile modificare la firma del metodo per accettare i parametri e infine catturare il blocco con un parametro che inizia con una e commerciale. Nell'esempio seguente il nostro blocco verrà catturato con il parametro di blocco & che invocheremo il metodo di chiamata.Questo è a posto di resa in

class Array 
    def modify_each!(add_one = true, &block) 
    self.each_with_index do |n, i| 
     j = (add_one) ? (i + 1) : i 
     self[i] = block.call(n,j) 
    end 
    end 
end 

Full article on ruby blocks

18

Blocchi, Procs e lambda (di seguito le chiusure in Informatica) sono uno degli aspetti più potenti di Ruby, e anche uno dei più fraintesi . Questo probabilmente perché Ruby gestisce le chiusure in un modo piuttosto unico. Rendere le cose più complicate è che Ruby ha quattro diversi modi di usare chiusure, ognuna delle quali è un po 'diversa, e talvolta senza senso. Ci sono alcuni siti con alcune ottime informazioni su come funzionano le chiusure all'interno di Ruby. Ma devo ancora trovare una buona guida definitiva là fuori.

class Array 
    def iterate!(&code) 
    self.each_with_index do |n, i| 
     self[i] = code.call(n) 
    end 
    end 
end 

array = [1, 2, 3, 4] 

array.iterate! do |n| 
    n ** 2 
end 

Procedure, AKA, Procs

blocchi sono molto pratico e sintatticamente semplice, ma si può decidere di avere molti blocchi diversi a nostra disposizione e utilizzare loro più volte. Come tale, passare lo stesso blocco ripetutamente ci richiederebbe di ripetere noi stessi. Tuttavia, poiché Ruby è completamente orientato agli oggetti, questo può essere gestito in modo abbastanza pulito salvando il codice riutilizzabile come un oggetto stesso. Questo codice riutilizzabile è chiamato Proc (abbreviazione di procedura). L'unica differenza tra blocchi e Proc è che un blocco è un processo che non può essere salvato e, in quanto tale, è una soluzione monouso. Lavorando con Procs, siamo in grado di iniziare a fare il seguente:

class Array 
    def iterate!(code) 
    self.each_with_index do |n, i| 
     self[i] = code.call(n) 
    end 
    end 
end 

array_1 = [1, 2, 3, 4] 
array_2 = [2, 3, 4, 5] 

square = Proc.new do |n| 
    n ** 2 
end 

lambda

Procs Finora, si è utilizzato in due modi, passandoli direttamente come attributo e salvandoli come variabile. Questi Proc sono molto simili a quelli che altre lingue chiamano funzioni anonime o lambda. Per rendere le cose più interessanti, anche i lambda sono disponibili all'interno di Ruby. Date un'occhiata:

class Array 
    def iterate!(code) 
    self.each_with_index do |n, i| 
     self[i] = code.call(n) 
    end 
    end 
end 

array = [1, 2, 3, 4] 

array.iterate!(lambda { |n| n ** 2 }) 

puts array.inspect 

Blocchi

Il più comune, più semplice e probabilmente più “Ruby come” modo di utilizzare le chiusure in Ruby è con i blocchi. Hanno la seguente sintassi familiare:

array = [1, 2, 3, 4] 

array.collect! do |n| 
    n ** 2 
end 

puts array.inspect 

# => [1, 4, 9, 16] 
+0

Ho visto un esempio simile qui http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/ Tuttavia sono confuso su cosa viene passato in '| n, i | 'è il valore e la posizione dell'indice all'interno dell'array? – Tom

+0

OK Ero stupido each_with_index è un metodo: 'each_with_index | item, index |' quindi non ho davvero bisogno di porre la domanda. – Tom

+1

Quando hai copiato questa risposta dal sito di robersosinki, hai dimenticato di includere le linee che passano il "quadrato" Proc a entrambi gli array - Ero leggermente confuso fino a quando non ho letto la sua versione originale. Forse indica la tua fonte la prossima volta. – moosefetcher

Problemi correlati