Translate

domingo, 14 de abril de 2024

Pattern Matching en Erlang parte 2


Iniciemos un nuevo módulo llamado funciones en el que escribiremos un montón de funciones para explorar Pattern Matching:


-module(functions).

-compile(export_all). %% replace with -export() later, for God's sake!


La primera función que escribiremos es head/1, que actúa exactamente como erlang:hd/1, que toma una lista como argumento y devuelve su primer elemento. Se hará con la ayuda del operador | :


head([H|_]) -> H.


Para obtener el segundo elemento de una lista crearíamos la función:


second([_,X|_]) -> X.


Erlang simplemente deconstruirá la lista con Pattern Matching

1> c(functions).

{ok, functions}

2> functions:head([1,2,3,4]).

1

3> functions:second([1,2,3,4]).

2


Crearemos una función same/2 que toma dos argumentos y dice si son idénticos:


same(X,X) ->

true;

same(_,_) ->

false.


Y es así de simple. Antes de explicar cómo funciona la función, repasaremos nuevamente el concepto de variables vinculadas y no vinculadas, por si acaso.

Las variables independientes son variables sin ningún valor asociado. Vincular una variable es simplemente adjuntar un valor a una variable independiente. En el caso de Erlang, cuando se desea asignar un valor a una variable que ya está vinculada, se produce un error a menos que el nuevo valor sea el mismo que el anterior. 

Volviendo a nuestro código: lo que sucede cuando llamas a Same(a,a) es que la primera X se ve como independiente: automáticamente toma el valor a. Luego, cuando Erlang pasa al segundo argumento, ve que X ya está vinculado. Luego lo compara con el a pasado como segundo argumento y mira para ver si coincide. La coincidencia de patrones se realiza correctamente y la función devuelve verdadero. Si los dos valores no son iguales, esto fallará y pasará a la segunda cláusula de función, a la que no le importan sus argumentos (cuando eres el último en elegir, ¡no puedes ser exigente!) y en su lugar lo hará. falso retorno. Tenga en cuenta que esta función puede aceptar cualquier tipo de argumento. Funciona para cualquier tipo de datos, no solo listas o variables individuales. Como ejemplo bastante avanzado, la siguiente función imprime una fecha, pero sólo si tiene el formato correcto:


valid_time({Date = {Y,M,D}, Time = {H,Min,S}}) ->

    io:format("The Date tuple (~p) says today is: ~p/~p/~p,~n",[Date,Y,M,D]),

    io:format("The time tuple (~p) indicates: ~p:~p:~p.~n", [Time,H,Min,S]);

valid_time(_) ->

   io:format("Stop feeding me wrong data!~n").


Tenga en cuenta que es posible utilizar el operador = en el encabezado de la función, lo que nos permite hacer coincidir tanto el contenido dentro de una tupla ({Y,M,D}) como la tupla en su conjunto. La función se puede probar de la siguiente manera:

4> c(functions).

{ok, functions}

5> functions:valid_time({{2011,09,06},{09,04,43}}).

The Date tuple ({2011,9,6}) says today is: 2011/9/6,

The time tuple ({9,4,43}) indicates: 9:4:43.

ok

6> functions:valid_time({{2011,09,06},{09,04}}).

Stop feeding me wrong data!

ok


¡Sin embargo hay un problema! Esta función podría tomar cualquier valor como valor, incluso texto o átomos, siempre que las tuplas tengan la forma {{A,B,C}, {D,E,F}}. Esto denota uno de los límites de Pattern Matching: puede especificar valores realmente precisos, como un número conocido de átomos, o valores abstractos, como el head o tail de una lista, una tupla de N elementos o cualquier cosa _ , etc. Para solucionar este problema utilizamos Guards.