Blocos em Ruby( lambda, proc, yield e tudo mais)
Seguindo a linha do último tutorial de ruby, hoje vamos falar de blocos. Vou tentar explicar de forma resumida e com exemplos o que são blocos e como usar. Lembro a primeira vez que vi um bloco em Ruby fiquei totalmente perdido pois ainda não tinha visto nada com uma sintaxe parecida em outras linguagens ( ex.: { |item| puts item } ). Blocos são confusos de entender a primeira vista pois acompanham um série de conceitos como closures, proc, lambda, Proc.new, yield …
Um bloco é um trecho de código Ruby que devem ser executado (dã). Melhorando a explicação com um exemplo:
1 2 3 4 5 6 |
["daniel","lopes"].each {|item| puts item} # => resultado sera: # => daniel # => lopes |
No exemplo acima, tenho um array e este array possui o método each. O método each recebe um bloco como parâmetro… ou seja, este bloco será executado a cada iteração em meu array.
Nosso bloco é formado por chaves que delimitam o inicio e o fim do bloco, um parâmetro ( |item| ) e sentença a ser executada ( puts item ). Também poderiamos delimitar o início e fim de nosso bloco com do end ao invés de {} , mas a convenção é que usamos {} para blocos de uma linha e blocos maiores com do end
No exemplo passamos o bloco para o método each e ele executou este bloco várias vezes, também optamos por passar um parâmetro para o bloco, mas podemos passar quantos parâmetros precisarmos. Vejamos agora o exemplo abaixo:
1 2 3 4 5 6 7 |
meu_hash = {:nome=>"daniel",:sobrenome=>"lopes"}
meu_hash.each {|index,item| puts "chave #{index},valor #{item}"}
# => resultado sera:
# chave nome,valor daniel
# chave sobrenome,valor lopes
|
Agora meu método each passa dois parâmetros para meu bloco, pois each agora pertence a um Hash e este é o comportamento do each em um hash, diferentemente de each em um Array, como vimos anteriormente.
Agora está claro que um bloco serve para armazenar quantas linhas de código quisermos, para ser executado em um certo momento e com seu próprio escopo. Portanto blocos são closures (fechamentos), pois eles estão fechados no ambiente em torno deles. Blocos também são chamados de nameless functions (funções sem nome).
Mas e se quisermos atribuir um bloco a uma variável? Não é possível, teremos um erro. Por definição blocos só são usados com métodos. Então para isso podemos usar o método lambda. O método lambda está presente no módulo Kernel do Ruby, (portanto não precisamos instancia nada quando o chamamos) e retorna um objeto da classe Proc, equivalente a Proc.new mas com algumas diferenças… confuso, né??? Então vamos ver um exemplo:
1 2 3 4 5 6 |
meu_bloco = lambda{ |param| puts "Hello #{param} !!!" }
meu_bloco.call("Daniel") #=> Hello Daniel !!!
meu_bloco.class #=> Proc
|
Então com lambda criamos uma instância da classe Proc. Proc objects são blocos de código vinculados a variáveis locais. Um Proc( procedures ) tem seu próprio escopo e pode ser chamado várias vezes em contextos diferentes e ainda ter acesso a estas variáveis. Então temos 3 formas de criar um Proc, com lambda, com proc (mesmo resultado que lambda) e com Proc.new … a diferença é que lambda e proc fazem checagem de parâmetro enquanto Proc.new não. Vamos ver um exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
bloco1 = lambda{|x,y| x+y}
bloco1.call(1,2) # => 3
bloco1.call(1) # erro por numero incorreto de parametros
ArgumentError: wrong number of arguments (1 for 2)
from (irb):1
from (irb):3:in `call'
from (irb):3
bloco2 = Proc.new {|x,y| x+y}
bloco2.call(2) # nao checa os parametros e tenta fazer o calculo com nil
TypeError: nil can't be coerced into Fixnum
from (irb):7:in `+'
from (irb):7
from (irb):8:in `call'
from (irb):8
from :0
bloco2.call(2,3,4,5) #=> 5
# na linha acima ignorou 4 e 5 que foram parametros a mais.
# abaixo a mesma coisa, mas usando proc
bloco3 = proc {|x,y| x+y}
bloco3.call(2,3,4,5) # erro por parametros
ArgumentError: wrong number of arguments (4 for 2)
from (irb):10
from (irb):11:in `call'
from (irb):11
from :0
|
Acho que agora ficou claro a diferença entre lambda,proc e Proc.new … mas ainda falta uma coisa, como faço para criar um método que aceite blocos como parâmetro? Não precisamos fazer nada, basta passar o bloco mas devemos saber quando aquele bloco vai ser executado dentro do método, para isso temos o yield. Vejamos o exemplo abaixo.
1 2 3 4 5 6 7 8 9 10 11 |
def executa_blocos puts "Olá, vou executar um bloco logo abaixo" yield puts "===============" end executa_blocos { puts "rodando bloco!" } # => Olá, vou executar um bloco logo abaixo # => rodando bloco! # => =============== |
O método yield espera um bloco, e no momento em que yield for chamado ele irá executar o bloco disponível. Agora um outro exemplo que pode gerar dúvidas:
1 2 3 4 5 6 7 8 9 |
def executa_blocos(vezes) vezes.times {yield} if block_given? end executa_blocos(2) { puts "rodando bloco!" } #=> rodando bloco! #=> rodando bloco! |
Então podemos passar parâmetros e também um bloco para um método. Mas ainda existe uma outra forma de passar um bloco como parâmetro, através do &, veja abaixo:
1 2 3 4 5 6 7 |
def executa_blocos(&meu_proc) yield end executa_blocos{ puts "rodando bloco!" } #=> rodando bloco! |
Desta forma o método aceita um bloco como parâmetro e vai convertelo em um Proc, mas de forma que não precisamos chamar o método call para executar pois temos o yield.
Bem, acho que é isso… espero ter deixado bem claro o que são blocos, lambda, proc, Proc.new, yield e parâmetros com &. Talvez eu tenha me esquecido de alguma coisa ou falado alguma bobeira(não sou nenhum expert em blocos :D), de qualquer forma postem nos comentários.
12 Comentários to “Blocos em Ruby( lambda, proc, yield e tudo mais)”
Junio Vitorino diz:
22/09/2008 em 08:52 PM
Du caralho mano, parabéns curti pakas o tutorial agora entendo porque do yield nas views. :P
Juarez P. A. Filho diz:
22/09/2008 em 10:39 PM
Daniel, gostei muito desse post… Eu estava justamente querendo aprender sobre isso, mas sabe como é a correria né? Foi muito esclarecedor e objetivo esse artigo. Valeu e até a próxima.
Mayron Cachina diz:
23/09/2008 em 01:24 PM
Muito interessante, vai ajudar bastante nos estudos!!! Estou passando a acompanhar o blog!
[]´s
Daniel Lopes diz:
23/09/2008 em 02:27 PM
Obrigado pessoal, da próxima vou tentar falar de class-eval e como ela pode quebrar a idéia de closures do ruby.
Davis Zanetti Cabral diz:
23/09/2008 em 03:46 PM
Parabéns pelo artigo. Muito bem organizado e as idéias mto bem apresentadas!
Silvio Fernandes diz:
23/09/2008 em 09:08 PM
Olá,
Muito bom o tutorial, bem objetivo mesmo. Como estou iniciando foi de grande ajuda, inclusive vou rever os codigos aqui da aplicação que estou desenvolvendo, para melhorar o codigo utilizando esse novo conhecimento. Sucesso pra voce!!!
[]`s
Herminio Torres diz:
04/11/2008 em 07:37 AM
Bem, se minha variável for um array ou um hash, eu posso fazer isso com o método collect, onde eu venha coletar o que eu desejo e o resultado jogar numa nova variável… num é possível? além do each, como foi citado ali acima, já que o mesmo não retorna nada.
Ricardo Amorim diz:
04/11/2008 em 07:54 AM
Parabéns, ótimo Post.
daniel lopes diz:
04/11/2008 em 10:38 AM
@Herminio, o collect executa o bloco para cada item do array e retorna um novo array com o valor retornado de cada iteração.
celio diz:
13/05/2009 em 08:50 AM
muito bom!!! tudo que eu tinha duvida!!!! valeu!!!!
Edgar diz:
04/06/2009 em 03:08 PM
Cara eu li o guia Why (http://why.nomedojogo.com) e não consegui entender sobre blocos, mas você explicou muito bem. Parabéns.
Arnaldo diz:
15/06/2009 em 09:29 AM
Vlw cara, ótimo post. Parabéns!!
Comentário
CATEGORIAS
HomeDesign
SEO
Empreendimento
Cifras
Ruby e Rails
Flex
Photoshop
Flash
XHTML/CSS
JavaScript
Variados
Database
Firefox
Projetos
3D
Projetos
TextMate
Smalltalk
Mac
Livros
ARQUIVO
03/2010 (2)02/2010 (7)
01/2009 (4)
12/2009 (7)
11/2009 (4)
10/2009 (10)
09/2009 (7)
08/2009 (6)
07/2009 (12)
06/2009 (5)
05/2009 (6)
04/2009 (9)
03/2009 (14)
02/2009 (18)
01/2009 (14)
12/2008 (20)
11/2008 (18)
10/2008 (9)
09/2008 (12)
08/2008 (6)
07/2008 (12)
06/2008 (10)
05/2008 (15)
04/2008 (19)




Botões de Logout
comentado por Leonardo
GAIA, o maior amigo do programador Flash
comentado por criação de sites
Seja produtivo consumindo tomates
comentado por Gabriel Sobrinho
Cifras: novas funcionalidades
comentado por Juarez P. A. Filho
Cifras: novas funcionalidades
comentado por Mário Santos