/* CODIFICANDO */

Relatos de um programador em contínua aprendizagem.

Archive for Agosto 2008

Métodos de classe adicionados através de modules

com um comentário

Para implementar algo semelhante à herança múltipla em Ruby usamos modules. Através deles podemos usar o conceito de mix-ins, ou seja, podemos misturar os comportamentos descritos na implementação de diversos modules em uma única classe concreta.
Algo assim:

module Bar
  def metodo_legal_de_bar
  end
end

module Foo
  def metodo_legal_de_foo
  end
end

class FooBar
  include Foo
  include Bar
end

f = FooBar.new
f.metodo_legal_de_bar
f.metodo_legal_de_foo

Simples, muito simples. Acabamos de criar dois modules que definem um método cada. O único detalhe é que estes dois métodos são métodos de instância, ou seja, são mensagens que só podem ser passadas para objetos da classe que inclui o módulo. E se eu quiser criar um módulo que define um método de classe que poderá ser utilizado em diversas outras classes?
Bom, o código abaixo não funciona

module Foo
  def self.um_metodo_de_classe_declararo_em_Foo
  end
end

class Blergh
  include Foo
end  

Blergh.um_metodo_de_classe_declararo_em_Foo
# NoMethodError: undefined method 'um_metodo_de_classe_declararo_em_Foo' for Blergh:Class

Hum… e agora, ferrou? Não caro amigo… Ruby é seu amigo e nada lhe faltará. Todo module possui um método muito interessante chamado included. Na verdade esse método é um “hook”, executado automaticamente sempre que um module é incluído em alguma classe. Ok, e para que serve esse treco? Como eu costumo dizer quando vejo algumas coisas em Ruby, serve para fazer macumba! Você pode criar certos comportamentos muito interessantes escrevendo código dentro do método included, como por exemplo resolver nosso problema de criação de métodos de classe através de modules.
O método included recebe um único argumento: A classe que está incluindo aquele método. Assim:

module Bla
  def self.included(base)
    puts "Acabei de ser incluido pela classe #{base}!"
  end
end

class Blergh
  include Bla
end

e só por fazer um “require” no arquivo que declara a classe Blergh você já vai ver no irb:

cassio@cassio-laptop:~$ irb
>> require 'exemplo_module'
Acabei de ser incluido pela classe Blergh
=> true

Interessante hein? Tudo bem, mas como vamos usar isso para “incrementar” nossa classe e criar novos métodos que podem ser chamados sem uma instância? Usamos o método extend. Este método adiciona os métodos de um module no nível da classe, ao contrário do método include, que adiciona os métodos de um module no nível de instância. A técnica a seguir é amplamente utilizada em Ruby para adicionar métodos de classe a uma classe existente, sendo inclusive amplamente utilizada dentro dos fontes do Rails:

module Bla
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def blabla
    end

    def blibli
    end
  end
end

Aqui foi declarado um module dentro de outro, algo bastante comum. Este module foi chamado de ClassMethods, mas qualquer outro nome poderia ter sido usado. É meio que uma convenção em Ruby chamar de ClassMethods, porque assim qualquer um que ler seu código vai entender que ali dentro estão sendo declarados métodos que serão adicionados às classes que incluirem o module em questão.
Quando o module é adicionado em alguma classe, o método included do module é chamado automaticamente e este por sua vez extende esta classe com os métodos declarados no module ClassMethods. Agora podemos fazer assim:

class Blergh
  include Bla
end

Blergh.blabla
Blergh.blibli

Agora, pensando em escopo: O que significa self dentro dos métodos declarados em ClassMethods? Hum… self é a própria classe que incluiu o module e que agora está chamando o método declararo em ClassMethods.

module Bla
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def blabla
      puts "Chamando o metodo de classe 'blabla' a partir da classe #{self}"
    end
  end
end

class Blergh
  include Bla
end

Blergh.blabla # <= Imprime "Chamando o metodo 'blabla' a partir da classe Blergh"

Com isso dá pra fazer muita macumba…

Escrito por cassiomarques

Agosto 22, 2008 em 2:32 am

Publicado em Desenvolvimento, ruby

Etiquetado com ,

Um pouco de Erlang, programação funcional e recursividade

com 16 comentários

Comecei a estudar um pouco de Erlang recentemente. A linguagem realmente é bem diferente daquilo que estou acostumado, principalmente por ser um linguagem totalmente funcional e possuir certos conceitos que em um primeiro momento realmente podem confundir a cabeça de quem está mais acostumado com linguagens imperativas.
Na verdade, eu já acabei usando diversos conceitos de programação funcional em Ruby e até mesmo em Javascript. A existência de closures ajuda muito para que isso seja possível e para muitos problemas uma solução funcional pode se apresentar a mais natural.
Entretanto, em Erlang a coisa é diferente. Tudo é baseado em programação funcional.
Uma das estrututas de dados mais comumente utilizadas em praticamente qualquer linguagem de programação são as listas. Quer essas listas sejam representadas por simples arrays ou por estruturas mais complexas como listas encadeadas, as listas estão presentes sempre que desejamos realizar qualquer tipo de operação sobre conjuntos.

Em Erlang, as listas são representadas através de estruturas do tipo [el1, el2, el3, ..., eln], onde eli pode assumir formas mais complexas, como em Lista = [{banana, 3}, {pera, 5}]. Neste exemplo estamos representando, por exemplo, uma lista de compras que contém 3 bananas e 5 pêras. O problema é não confundir as palavras “banana” e “pera” com strings ou variáveis: neste exemplo, banana e pera são atoms, ou seja, apenas identificadores para elementos da lista.
Outro detalhe importante é que em Erlang um símbolo somente será considerado como uma variável se começar por uma letra maiúscula.

Mas vamos ver então em detalhes como Erlang opera sobre listas!

Vamos partir de um exemplo bastante simples. Suponha que você possui uma lista de números inteiros e deseja calcular o produtório desta lista, ou seja, sair multiplicando número por número até chegar em um total.

Em uma linguagem imperativa como C, isso poderia facilmente ser feito assim:

#include <stdio.h>

int main()  {
  int lista[5] = {3, 5, 7, 2, 8};
  int prod = 1, i = 0;
  for(i = 0; i < 5; ++i) { prod *= lista[i]; }
  printf("Produto = %d\n", prod);
}

Até aqui, ok, Basta fazermos um laço e sair multiplicando os números dentro do array prod que representa nossa lista.

Mas e em Erlang, como ficaria isso? Bom, primeiro precisamos entender como Erlang processa os elementos de uma lista. Em Erlang, pode-se assumir que toda e qualquer lista é formada de duas partes: HEAD e TAIL. HEAD é sempre o primeiro elemento da lista, ou seja, um escalar. Já TAIL representa o restante da lista. Assim, na lista [1,2,3,4] temos HEAD = 1 e TAIL = [2,3,4]. Na verdade os nomes HEAD e TAIL são apenas convenções, não é obrigatório que você use estes termos para representar as duas “partes” das listas em Erlang. Mas convenções são boas e facilitam a compreensão do código que escrevemos em qualquer linguagem.
Como podemos então extrair e armazenar em variáveis os valores de HEAD e TAIL de uma lista? Usando pattern matchers. Na verdade, em Erlang todas as atribuições de valores a variáveis são feitas através da utilização de pattern matchers. O operador utilizado para isso é o =. Mas aqui, = tem um significado diferente do que você está acostumado! Sim! Igual não é mais igual e o mundo está perdido…
A verdade é que em Erlang = não representa necessariamente igualdade. = serve para “casar” padrões. Cada variável pode armazenar um determinado padrão e você pode utilizar o operador = para verificar se este padrão “casa” com algum outro e então fazer associações de valores dentro destes padrões. Assim:

%% Em Erlang, linhas que começam com % são comentários.
%% Todas as linhas que representam "comandos" devem terminar com um ponto
1> X = 2.

Ok, agora a variável X vale 2. Mas isso porque antes disso não associamos nenhum outro valor a X, ela era uma variável “vazia”, ou, usando a terminologia do Erlang, X era uma unbound variable. Agora, se tentarmos alterar o valor de X, olhe o que acontece:

2> X = 4.
=ERROR REPORT==== 17-Aug-2008::14:34:44 ===
Error in process  with exit value: {{badmatch,4},[{erl_eval,expr,3}]}

** exited: {{badmatch,4},[{erl_eval,expr,3}]} **

Que erro bizarro! Que porcaria de linguagem é essa que não me permite alterar o valor de uma variável? O que ocorre é que Erlang é uma linguagem para processamento paralelo, onde diferentes processos trocam mensagens entre si. Se temos por exemplo 3 processos A, B e C que se comunicam entre si e uma variável X que é usada pelos 3 processos, se A passar o valor para B e este alterá-lo, quando C for usar esta variável pode ser que ocorram resultados inesperados. Por isso Erlang mantém as variáveis imutáveis durante toda sua existência.
Partindo deste princípio, podemos fazer o seguinte:

1> X = 2. %%X era 'unbound' e acabou de receber seu valor (2)
2> X = 1 + 1. %% funciona, porque 1 + 1 representa o mesmo padrão que 2

Ok, vamos parar de enrolar e voltar para a nossa lista, de onde queremos extrair nossas partes HEAD e TAIL.

Podemos usar o operador | (nosso velho amigo ‘pipe’) para fazer o trabalho. Podemos então escrever:

Numeros = [3, 5, 7, 2, 8].
[H|T] = Numeros.

E após isso teremos a variável H armazenando 3 (nosso HEAD) e a variável T armazenando [5, 7 , 2, 8] (o resto da lista ou TAIL).

Para podermos calcular nosso produtório precisamos ainda dar um olhadinha em como funcionam funções em Erlang. O processo todo funciona através da utilização de modules. Um module é um elemento de software onde você pode declarar suas funções. Em seguinda você precisa exportar essas funções para que as mesmas possam ser utilizadas por código fora do módulo. Algo assim:

-module(produtorio).
-export([prod/1]).
prod([H|T]) -> H * prod(T);
prod([])      -> 1.

Na primeira linha estamos fazendo a declaracao de um modulo chamado “produtorio”. Na segunda linha estamos exportando uma função chamada “prod” que recebe um único argumento (por isso o “/1″), para que a função possa ser usada por código em outras partes do programa.
A terceira e quarta linhas definem nossa função prod, a qual efetivamente realiza o nosso cálculo. Para explicar o que está acontecendo aqui, precisamos relembrar alguns conceitos de recursividade.
Uma função recursiva é uma função que, para atingir seu objetivo, realiza chamadas a si mesma. Assim, podemos por exemplo escrever uma função recursiva soma([1,2,3]) como soma(1, soma([2,3])).

Por definição, uma função recursiva deve possuir duas características:
1) Deve possuir uma solução trivial, ou seja, uma situação onde a função pára de chamar a si mesma recursivamente e a pilha de chamadas passa a ser desfeita, até a primeira chamada feita, onde o resultado finalmente é retornado ao ponto do código que fez a chamada a nossa função.
2) A função deve ser escrita de forma que caminhe naturalmente para a solução do problema ou, em outras palavras, a função deve ser convergente, caso contrário as chamadas recursivas ocorrerão infinitamente, levando a um estouro na pilha de chamadas da função.

Essas duas características podem ser facilmente observadas na nossa função prod acima. Na linha

prod([H|T]) -> H * prod(T);

temos a forma em que as chamadas recursivas ocorrerão, o que demonstra claramente que há convergência para a solução. A função recebe uma lista de valores, extraí o primeiro elemento da lista e retorna como resultado desta chamada específica o resultado da multiplicação deste elemento pelo resultado de uma chamada a si mesma, passando o restante da lista (ou TAIL) como argumento. É fácil observar que a cada nova chamada, TAIL diminui, até chegar a 0 (zero) elementos. Essa é a nossa solução trivial, encontrada pela linha

prod([])    -> 1.

Aqui, não é mais necessário realizar chamadas recursivas, pois sabe-se que para a lista vazia basta multiplicar por 1, ou seja, o elemento neutro da multiplição. A partir deste ponto a pilha de chamadas recursivas passa a ser esvaziada até chegarmos à sua basse, ou seja, a primeira chamada feita à função prod().

Por fim, podemos usar nossa função assim (através do erl, o console do Erlang):

1> c(produtorio).
{ok,produtorio}
2> Lista = [3,6,8,4,7].
[3,6,8,4,7]
3> P = produtorio:prod(Lista).
4032
4>

Em (1) estamos compilando nosso module produtorio para um arquivo com extensão .bean, para que possamos usá-lo por todo nosso programa. Em (2) declaramos uma variável Lista, que representa uma lista de valores sobre os quais desejamos calcular o produtório. Em (3) realizamos efetivamente a chamada à função prod. A sintaxe produtorio:prod indica que a função prod faz parte do module produtorio. Você pode pensar nisso como um esquema de namespaces.
O cálculo ocorre seguindo a seguinte lógica:

Chamada 1) HEAD -> 3, TAIL -> [6,8,4,7], retorno -> 3 * prod([6, 8, 4, 7])
Chamada 2) HEAD -> 6, TAIL -> [8,4,7], retorno -> 18 * prod([8,4,7])
Chamada 3) HEAD -> 8, TAIL -> [4, 7], retorno -> 144 * prod([4, 7])
Chamada 4) HEAD -> 4, TAIL -> [7], retorno -> 576 * prod([7])
Chamada 5) HEAD -> 7, TAIL -> [], retorno -> 4032 * prod([])
Chamada 6) Utiliza-se a solução trivial, retornando-se 4032 * 1 e dando fim à cadeia de chamadas recursivas.

Conforme eu for estudando mais coisas sobre Erlang eu vou postando aqui o que achar interessante!

Escrito por cassiomarques

Agosto 17, 2008 em 10:58 pm

Publicado em Desenvolvimento, Erlang

Etiquetado com