Conceitos na prática: Ruby lambda

Olá! Neste post vou falar sobre lambdas em ruby e mostrar alguns exemplos de sua utilidade. No post anterior, falamos um pouco sobre como blocos podem ser úteis para generalizar a implementação de um código. Hoje vamos brincar um pouco com lambdas e como utilizá-las como blocos.

O que são Lambdas

Em ruby é possível criar métodos. Um método contém um conjunto de instruções que serão executadas quando este for invocado. Nesse sentido, lambdas são bem parecidos com métodos. Vamos ver um pouco da sintaxe:

puts_lambda = lambda do
  puts "lambda"
end

puts_lambda.call

Uma primeira observação é que o lambda foi atribuído para uma variável. Isso mostra que podemos armazenar os lambdas em variáveis para executar depois.

A sintaxe para criação de um lambda é bem simples, basta chamar “lambda” e passar um bloco qualquer. Mais tarde, para executar o bloco, basta chamar o método “call”.

Nas verões mais recentes de ruby, podemos criar lambdas utilizando o operador “->”, que é menor do “lambda” mas que não tão legível. O código a seguir é equivalente ao anterior:

puts_lambda = -> do
  puts "lambda"
end

puts_lambda.call

Lambdas e argumentos

Lambdas também podem receber argumentos, assim como métodos. A diferença é que precisamos adicionar argumentos ao bloco, semelhante a como fizemos com o método “for_each” no post anterior.

puts_lambda = lambda do |msg|
  puts "#{msg}"
end

puts_lambda.call "Lambda!"

Agora qualquer objeto que for passado ao lambda será exibido no console. Se tentarmos executar o lambda sem parâmetros, uma exceção “ArgumentError: wrong number of arguments (0 for 1)” será lançada.

Assim como métodos também podemos atribuir um valor default para os parâmetros que devem ser opcionais.

puts_lambda = lambda do |msg = "lambda"|
  puts "#{msg}"
end

puts_lambda.call
puts_lambda.call "Lambda!"

Lambdas como blocos

Agora que conhecemos a sintaxe do lambda, vamos brincar um pouco. No post anterior vimos que é possível passar um bloco para o método “each” de um array. Pois bem, como lambdas armazenam um bloco, também podemos passar lambdas como blocos:

puts_lambda = lambda do |msg|
  puts "#{msg}"
end

[1,2,3,4,5].each puts_lambda #=>ArgumentError: wrong number of arguments (1 for 0)

Ao executar o código acima, uma exceção será lançada. Pela mensagem podemos ver que o método each não aceita argumentos, pois passamos 1 argumento enquanto nenhum era esperado. Então como podemos passar um bloco como argumento para o “each” se ele não aceita nenhum argumento?

Lembra que no post anterior vimos que não é necessário explicitar que um método em ruby recebe um bloco como argumento? Que basta passar o bloco e executar um “yield” para que o bloco seja chamado?

Pois bem, ao criar um lambda usando o irb, podemos ver que o terminal mostra “Proc:0x007ff52397a8b8@(irb):6 (lambda)” como retorno. Ou seja, lambdas são na verdade objetos da classe Proc! Se tentarmos chamar o método “class” em um objeto lambda temos como retorno “Proc”.

No entanto existem algumas pequenas diferenças entre Proc e lambdas que serão faladas em posts futuros. Por hora precisamos passar o lambda como bloco para o método “each” de um array.

A maneira como podemos converter lambdas (e procs) em blocos é utilizando o caractere “&” logo atrás do objeto. Como no seguinte código:

puts_lambda = lambda do |msg|
  puts "#{msg}"
end

[1,2,3,4,5].each &puts_lambda

Operado Ampersand ou “&”

O operador “&” vai converter objetos proc em bloco ou bloco em proc. A útilidade de converter um bloco em proc é quando você tem um método, que deve receber um bloco como parâmetro e precisa passar esse bloco para outro método.

Imagine que o método “for_each” do post anterior agora vai simplesmente passar o bloco para o método each, ao invés de chamar o yield:

def for_each(array, &block)
  array.each &block
end
 
for_each [1,2,3,4,5] do |e|
  puts e
end

No parâmetro, recebemos o bloco com o operador “&”, dizendo que o bloco deve ser convertido em proc. Depois, com o objeto block em mãos, passamos para o método “each” também utilizando o operador “&”, pois queremos que o objeto proc seja convertido em bloco para o método each.

Agora, se quisermos que o método “for_each” receba um lambda como argumento, não é necessário o primeiro “&” (na lista de argumentos). Como lambda já são procs, não precisamos converter nada e a implementação seria a seguinte:

puts_lambda = lambda do |msg|
  puts "#{msg}"
end

def for_each(array, lambda)
  array.each &lambda
end
 
for_each [1,2,3,4,5], puts_lambda

Apesar de parecer complicado, na prática lambdas ajudam bastante a generalizar código para que fique mais fácil reutilizá-lo. Vale a pena quebrar um pouco a cabeça para entender lambdas.

No próximo post vou falar um pouco sobre Procs e mostrar algumas diferenças dos lambdas. Até lá!

Referências

[1] Ahsan Sharafuddin: Closures in Ruby, http://technicalecstasy.net/closures-in-ruby-blocks-procs-and-lambdas/

Anúncios

Um comentário sobre “Conceitos na prática: Ruby lambda

  1. Muito bom! Obrigado por compartilhar !

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s