Programação sequencial
Nomenclatura de conjuntos (compreensão de listas)
O Erlang permite utilizar uma notação sucinta, baseada na "Set
Compreension" da teoria de conjuntos de Zermelo-Frankel, para gerar uma
nova lista:
[Expr || Condição 1, ..., Condição
N]
Expr
é uma expressão que será aplicada a todos os elementos da lisa.
Condição é um gerador ou um
filtro:
Padrão <- Lista
Regras a respeitar no uso de compreensão de listas:
Existem dois operadores especiais:
L = [1,2,3] ++ [4,5,6]
L = [1,2,3,4,5,6]
L = [1,2,3,4,1] -- [1,4]
L = [2,3,1]
[X||X<-Lista,Condição
1,...Condição N]
(lê-se X tal que X pertence a
Lista e Condição 1, ...)
O resultado do código anterior é uma lista composta pelos elementos que respeitam as condições especificadas.
1> L = [1,2,3,4,5,6,7,8,9,10].
[1,2,3,4,5,6,7,8,9,10]
2> [X||X<-L,X>5].
[6,7,8,9,10]
» Exemplo 2
1> L = [1,2,3].
[1,2,3]
2> [X*2 || X<- L].
[2,4,6]
3> L2 = [1,2,3,4.0,a].
[1,2,3,4.0,a]
4> [X*2 || X <- L2, is_integer(X)].
[2,4,6]
» Exemplo 3
select(X,L) -> [Y || {X,Y} <- L].
Ao compilar é gerado o seguinte erro:
Warning: variable 'X' shadowed in generate
Problema: X no gerador não é o mesmo X do cabeçalho da função!
Resultado:
L = select(b, [{a,1}, {b,2}, {c,3}, {b,4}]
L = [1,2,3,4]
Solução: Gerador deve ter variáveis não instanciadas
select(X,L) -> [Y || {X1,Y} <- L, X1 == X].
Resultado:
L = select(b, [{a,1}, {b,2}, {c,3}, {b,4}]
L = [2,4]
Exercícios
Implemente as seguintes funções usando compreensão de listas:
Records
Os records em Erlang são
estruturas de dados semelhante às structs em C. Como vimos, os
tuplos apenas permitem referenciar elementos pelo seu número, usando a
instrução element(N,Tuplo). Se mudarmos a ordem dos elementos,
retirarmos um elemento ou adicionarmos um elemento temos de alterar o
código do programa. Por sua vez, os records permitem
referenciar
os elementos pelo nome,
permitindo ao programador ignorar a ordem dos elementos.
Definir um record
Um record é definido da seguinte forma:
-record(nome_do_record,
{campo1 [= Valor1],
campo2 [= Valor2],
...
campoN [= ValorN]}).
O nome dos records e dos campos deve ser um átomo. Os valores por omissão são opcionais e sempre que não existirem o campo toma o valor undefined.
Exemplo
-record(pessoa,{nome,telefone,endereco}).
#nome_do_record{campo1 = Valor1, campo2 = Valor2, ..., campon = ValorN}
Se algum dos campos for omitido é usado o valor por omissão.
Exemplo
>P = #pessoa{nome = "Joao", telefone = '123456789'}
{pessoa, "Joao", '123456789', undefined}
Aceder a um campo
Se P for uma variável do tipo record pessoa, podemos
usar a seguinte sintaxe:
Nome = P#pessoa.nome,
Endereço = P#pessoa.endereco
Actualizar um record
>P = #pessoa{nome = "Joao", telefone = '123456789', endereco = {rua xpto,porto}}
{pessoa,"Joao",'123456789',{rua xpto,porto}}
>P2 = P#pessoa{nome = "Joana"}
{pessoa,"Joana",'123456789',{rua xpto,porto}}
Se um record for usado em vários módulos, a sua definição deve ser
colocada num ficheiro com a extensão '.hrl'. Os módulos acedem à sua
definição através de:
-include("os_meus_records.hrl").
Exercícios
Primitivas case
e if
case Expr of
Padrão 1 [when Guard1] -> Sequência 1;
Padrão 1 [when Guard1] -> Sequência 1;
...
Padrão N [when GuardN] -> Sequência N
end.
Expr pode ser uma expressão
aritmética, uma BIF ou uma função do utilizador. A utilização de guards é opcional. Expr é avaliada e os n padrões são
sequencialmente comparados com o resultado. Se um dos padrões é
verdadeiro então a sequência correspondente é avaliada.
Pelo menos um dos padrões tem de ocorrer, caso contrário é gerado um
erro de run-time.
Exemplo:
validar(Msg) ->
case Msg of
{msg1,From,Data} ->
tratar_msg1(Msg);
{msg2,From,Data}
tratar_msg2(Msg);
Else ->
'msg desconhecida'
end.
Como se pode ver o case
pode facilmente ser substituído por várias clausulas de uma função.
If
if
Padrão 1 -> Sequência 1;
Padrão 2 -> Sequência 2;
...
Padrão N -> Sequência N
end.
Tal como no case pelo
menos
um dos padrões tem de ocorrer, caso contrário é gerado um erro de
run-time.
Exemplo:
maior_ou_igual(X,Y) ->
if
X > Y ->
verdadeiro;
true ->
falso
end.
O átomo true pode ser usado como um else.