jueves, 27 de mayo de 2010

Tutorial de Erlang (1) Básico


Este es el primero de una serie de guías esquemáticas que realizaré mientras me introduzco en el apasionante mundo de Erlang:




1. Funciones y módulos

Con este primer programa veremos como se desarrolla con erlang:

test1.erl:
---------------------------------------------
-module(test1).
-export([sumar/2, restar/2, factorial/1]).

% Funcion sumar
sumar(X, Y) ->
X + Y.
% Funcion restar
restar(X, Y) ->
X - Y.

% Funcion factorial
factorial(1) ->
1;
factorial(X) when X > 0 ->
X * factorial(X - 1).
---------------------------------------------


Situar el fichero test1.erl en el bin donde reside el programa erl (erl.exe en windows).

---------------------------------------------
C:\Program Files\erl5.7.5\bin>erl.exe
Eshell V5.7.5 (abort with ^G)
1> c(test1).
{ok,test1}
2> test1:sumar(3,5).
8
---------------------------------------------

Con c(test1). realizamos la compilación del módulo.


Elementos que hemos visto:

- module(test1)
Nombre del módulo. El fichero que lo contenga debe llamarse igual que el módulo con la extensión .erl. Para llamar a las funciones que exporte el módulo la sintaxis es modulo:funcion(param1, param2).

-export([sumar/2, restar/2]).
Exportación de funciones del módulo al exterior con el número de parámetros esperados

factorial
Define dos comportamientos en función del parámetro de enrada para limitar la recursividad, el ";" separa las múltiples definiciones de la misma función.

%
Comentario, acaban con el retorno de carro


2. Variables, Atomos, Tuplas y Listas


Variables
Empiezan por mayúscula, pueden asignarse, pero sólo una vez.

Atomos
Comienzan por minúscula y se reducen a un literal que podrá ser utilizado en el programa.

Tuplas:
Compone un tipo de dato mediante un número fijo de términos:
{Term1,...,TermN}
Cada término en la tupla se llama elemento. El número de elementos es el tamaño de la tupla.
Hay funciones predefinidas para manejarlas (BIFs: built-in functions).

Examples:

1> P = {adam,24,{july,29}}.
{adam,24,{july,29}}
2> element(1,P).
adam
3> element(3,P).
{july,29}
4> P2 = setelement(2,P,25).
{adam,25,{july,29}}
5> tuple_size(P).
3
6> tuple_size({}).
0

Listas:
Compone un tipo de datos mediante un número variable de términos
[Term1,...,TermN]
Cada término de la lista se llama elemento. El número de elementos es la longitud de la lista.

Examples:

37> L = [a, b, {1, 2}].
[a,b,{1,2}]
38> [H|T] = L.
[a,b,{1,2}]
39> H.
a
40> L.
[a,b,{1,2}]
41> T.
[b,{1,2}]
42> length(H).
** exception error: bad argument
in function length/1
called as length(a)
43> length(T).
2
44> length(L).
3
45> L2 = [a, b | T].
[a,b,b,{1,2}]
46> L2.
[a,b,b,{1,2}]
47> T.

test2.erl:
---------------------------------------------
-module(test2).
-export([contar_lista/1, invertir_lista/1]).

% Funcion contar_lista
contar_lista([]) ->
0;
contar_lista([H|T]) ->
1 + contar_lista(T).

% Funcion invertir_lista interfaz
invertir_lista(L) ->
invertir_lista(L, []).

% Funcion invertir_lista implementacion con variable auxiliar de arrastre
invertir_lista([H|T], Lista_Invertida) ->
invertir_lista(T, [H | Lista_Invertida]);
invertir_lista([], Lista_Invertida) ->
Lista_Invertida.
---------------------------------------------


3. Salida por el terminal


1> io:format("salida").
salidaok
2> io:format("salida~n").
salida
ok
3> io:format("salida ~w~n", [hola]).
salida hola
ok
4> io:format("salida ~w ~w ~w~n", [hola, adios, 1]).
salida hola adios 1
ok


4. Control de flujo (if/case)

test3.erl:
---------------------------------------------
-module(test3).
-export([encontrar_elemento/2, encontrar_elemento_case/2]).

% Funcion encontrar_elemento
encontrar_elemento(A, []) ->
false;
encontrar_elemento(A, [H|T]) ->
if
A == H ->
true;
% El else aqui seria true ->
A =/= H ->
encontrar_elemento(A, T)
end.
encontrar_elemento_case(A, []) ->
false;
encontrar_elemento_case(A, [H|T]) ->
case A of
H ->
true;
_Else ->
encontrar_elemento_case(A, T)
end.
---------------------------------------------

5. Expresiones


5.1. Comparación de términos

Expr1 op Expr2
op Descripción
---------------------------
== igual
/= distinto
=< menor o igual
< menor
>= mayor o igual
> mayor
=:= exactamente igual
=/= exactamente distinto


5.2. Operadores aritméticos

op Expr
Expr1 op Expr2

op Descripción Tipo de arg
-----------------------------------------------
+ operador unario + number
- operador unario - number
+ number
- number
* number
/ división en coma flotante number
bnot not unario a nivel de bit integer
div división entera integer
rem resto entero de X/Y integer
band and a nivel de bit integer
bor or a nivel de bit integer
bxor xor a nivel de bit integer
bsl desplazamiento a la izq. integer
bsr desplazamiento a la dcha. integer


5.3. Expresiones booleaneas

op Expr
Expr1 op Expr2

op Descripción
---------------------
not unario not
and and lógico
or or lógico
xor xor lógico


5.4. Expresiones (short-circuit) evaluación perezosa

Sólo se evalúa la 2 si es estrictamente necesario.

Expr1 orelse Expr2
Expr1 andalso Expr2


5.5. Operaciones sobre listas

Expr1 ++ Expr2
Concatenación de listas
1> [1,2,3]++[4,5].
[1,2,3,4,5]

Expr1 -- Expr2
Substracción, elimina la primera aparición de cada elemento de la segunda lista de la primera, opera en modo foreach
1> [1,2,3,2,1,2]--[2,1,2].
[3,1,2]


5.6. Guard Sequences (secuencias de guardia)

Una secuencia de guardia es una secuencia de guardias, separadas por punto y coma (;). La secuencia de guardia es true si al menos una de sus guardias (elementos) es true. Cuando una lo cumple, las restantes, si las hubiera, no son evaluadas:

Guard1;...;GuardK

Una guardia es una secuencia de expresiones de guardia separadas por coma (,). La guardia es true si todas las expresiones evaluadas son true

GuardExpr1,...,GuardExprN

Expresiones válidas de guardia son las siguientes:

El atom true, otras constantes (términios y variables establecidas), comparaciones de términos, expresiones aritméticas, booleanas y short-circuit, y las siguientes funciones predefinidas:

BIFs de chequeo de tipos
-------------------------
is_atom/1
is_binary/1
is_bitstring/1
is_float/1
is_function/1
is_function/2
is_integer/1
is_list/1
is_number/1
is_pid/1
is_port/1
is_record/2
is_record/3
is_reference/1
is_tuple/1

BIFs aritméticas
--------------------------
abs(Number)
bit_size(Bitstring)
byte_size(Bitstring)
element(N, Tuple)
float(Term)
hd(List)
length(List)
node()
node(Pid|Ref|Port)
round(Number)
self()
size(Tuple|Bitstring)
tl(List)
trunc(Number)
tuple_size(Tuple)


5.7. Funciones de orden superior

fun
(Pattern11,...,Pattern1N) [when GuardSeq1] ->
Body1;
...;
(PatternK1,...,PatternKN) [when GuardSeqK] ->
BodyK
end

Una función de orden superior (fun expression) empieza con la palabra reservada "fun" y acaba con la palabra reservada "end". Entre ellas aparece la declaración de la función, similar a una declaración de función estandar excepto que no aparece el nombre de la función especificado.

1> Fun1 = fun (X) -> X+1 end.
#Fun
2> Fun1(2).
3
3> Fun2 = fun (X) when X>=5 -> gt; (X) -> lt end.
#Fun
4> Fun2(7).
gt

foreach y map son dos funciones estandar que operan de este modo, la primera aplica la función a cada elemento de la lista y la segunda genera una nueva lista como resultado de aplicar la función a cada elemento de la lista.

92> Add 3 = fun(X) -> X + 3 end.
#Fun
93> lists:map(Add 3, [1,2,3]).
[4,5,6]



6. Programación concurrente


6.1. Procesos

Los procesos Erlang son como los threads pero sin datos. La función predefinida "spawn" es utilizada para crear nuevos procesos.

spawn(Módulo, Función exportada, Lista de argumentos)

Retorna el identificador del proceso Pid.

test4.erl:
--------------------------------------------
-module(test4).
-export([start/1, funcion/2]).

funcion(Palabra, Proceso) ->
io:format("~w - ~w~n", [Palabra, Proceso]).

start(0) ->
done;
start(A) ->
Pid1 = spawn(test4, funcion, [hola, A]),
io:format("Proceso ~w arrancado~n", [Pid1]),
Pid2 = spawn(test4, funcion, [adios, A]),
io:format("Proceso ~w arrancado~n", [Pid2]),
start(A - 1).
--------------------------------------------


6.2. Paso de mensajes:

--------------------------------------------
-module(test5).
-export([start/0, funcion/0]).

funcion() ->
receive Mensaje ->
io:format("Proceso recibe el mensaje: ~w~n", [Mensaje]),
funcion()
end.

1>Pid = spawn(test5, funcion, []).

2>Pid ! hola
3>Pid ! adios
--------------------------------------------

Patrón de recepción de mensajes:

receive
patron1 ->
acciones1;
patron2 ->
acciones2;
....
patronN ->
accionesN
end.

Paso de mensaje: Pid ! mensaje

self(): Función que da el Pid del proceso actual


6.3. Registrar nombres de procesos

register(pong, spawn(tut16, pong, []))

pong ! mensaje

register(Name, Pid) Asocia un átomo con un Pid
registered() Retorna una lista de nombres registrados.
whereis(Name) Retorna el Pid del nombre buscado si está registrado


No hay comentarios:

Publicar un comentario