Mostrando las entradas con la etiqueta erlang. Mostrar todas las entradas
Mostrando las entradas con la etiqueta erlang. Mostrar todas las entradas

domingo, 13 de diciembre de 2015

Erlang - Sintaxis en funciones

Que sucede con el if?
El condicional if  actúa como las guardas y comparte con ellas la sintaxis pero fuera de la clausula cabecera  de la funcion. Las clausulas if son llamada Patrones de Guardas. Los IFs en Erlang son diferentes de los ifs  que encuentras en la mayoria de los lenguajes. Cuando entrás al pais de Erlang deberías dejar en la puerta todo lo que sabes acerca de los ifs

Para ver que tan similar a las guardas es la expresión if, mira lo siguiente.

-module(what_the_if).
-export([heh_fine/0]).

heh_fine()->
    if 1 =:= 1 ->
        works
    end,
    if 1=:= 2; 1 =:= 1->
        works
    end,
    if 1 =:= 2, 1=:= 1 ->
        fails
    end.


guarda esto como what_the_if.erl y probemos.

1> c(what_the_if).
./what_the_if.erl:12: Warning: no clause will ever match
./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false' {ok,what_the_if}
2> what_the_if:heh_fine().
** exception error: no true branch found when evaluating an if expression
        in function what_the_if:heh_fine/0


Bueno, el compilador nos informa que hay un error en la linea 12 que informa que esa linea siempre se evalua como falso. Recuerda, que en Erlang, todo debe retornar algo y la expresión if no es una excepción a la regla. De esta manera cuando Erlang no puede encontrar una manera de tener una guarda exitosa, este se rompe: no puede no devolver algo. Así que necesitamos agregar una rama que capture todo haciendo que siempre sea exitoso de cualquier manera.

En cualquier lenguaje esto es llamado un "else". En Erlang usamos "true" (esto explica por que la VM lanza el error "no hay rama verdadera")

oh_god(N) ->
    if N =:= 2 -> might_succeed;
        true -> always_does
    end.

Y ahora si nosotros testeamos esta función (la parte anterior seguirá lanzando esos warnings)

3> c(what_the_if).
./what_the_if.erl:12: Warning: no clause will ever match
./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false' {ok,what_the_if}
4> what_the_if:oh_god(2).
might_succeed
5> what_the_if:oh_god(3).
always_does


Seguramente serás alguno de los programadores que se sorprende por el uso de atomo true en vez de else para el control de flujo, despues de todo es más familiar. Como dice Richard O'Keefe básicamente usar el true es capturar todas las otras posibilidades y estos ejemplos que dá él mismo deberián solucionar nuestra confusión.

                          by
 if X > Y -> a()  if X > Y  -> a()
  ; true  -> b()   ; X =< Y -> b()
 end        end

 if X > Y -> a()  if X > Y -> a()
  ; X < Y -> b()   ; X < Y -> b()
  ; true  -> c()   ; X ==Y -> c()
 end   end 
 

Las ramas "else" o "true" deberián ser evitadas ambas. Los ifs son usualmente más fácil de leer cuando cubres todas los extremos lógicos que cuando confias eso a una clausula "captura-todo".

En caso ... de

Si la expresión if es como una guarda una expresión case ... of  es como la cabecera de la función completa: puede tener una compleja coincidencia de patrones para cada argumento, y puedes tener guardas encima de él.

Probablemente estás familiarizado con la sintaxis, no necesitaremos demasiados ejemplos. Para esto, escribiremos una función para agregar elementos a un conjunto (una colección de valores únicos) que podremos representar como una lista desordenada. Esta posiblemente sea la peor implementación en términos de eficiencia. Aquí la sintaxis.

insert(X, []) -&gt
    [X];
insert(X,Set) -&gt
    case lists:member(X,Set) of
        true -&gt Set;
        false -&gt [X|Set]
    end.

Si enviamos una lista vacia y un término x para ser agregado, entonces nos retornará una lista solo conteniendo el valor x. De otra manera la función lists:member/2 verifica si un elemento es parte de un lista y retorna true si existe o false si no lo está. En caso de que ya exista el elemento en el conjunto entonces no lo modificamos. Sino, agregamos x al conjunto como el primer elemento de la lista.

En este caso la coincidencia de patrones, fue realmente simple. Aquí una más compleja,

beach(Temperature) -&gt
    case Temperature of
        {celsius, N} when &gt= 20, N =&lt 45 -&gt
            'favorable';
        {kelvin, N} when N &gt= 293, N =&lt 318 -&gt
            'scientifically favorable';
        {fahrenheit, N} when N &gt= 68, N =&lt 113 -&gt
            'favorable in the US';
        _ -&gt
            'avoid beach'
    end.

Aquí la respuesta a "es el momento adecuado para ir a la playa?", dadas 3 temperaturas diferentes: grados Celsius, Kelvin, Fahrenheit. La coincidencia de patrones y guardas son combinadas en orden para devolver una respuesta satisfactoria a todos los usos. Como apuntamos anteriormente, las expresiones "case ... of" son la misma cosa que varias cabeceras de funciones con guardas. De hecho podriamos reescribir nuestro código de la siguiente manera.

beachf({celsius, N}) when N &gt= 20, N =&lt 45 -&gt
    'favorable';
...
beachf(_) -&gt
    'avoid beach'.

Esto genera la siguiente pregunta. Cuando deberiamos usar "if", "case ... of" o funciones para hacer expresiones condicionales.

Que utilizar?

Que utilizar es una pregunta dificil. La diferencia entre llamadas a funciones y 'case ... of' son minimas: de hecho son representadas de la misma manera a bajo nivel. y usar una o la otra tiene el mismo costo en términos de rendimiento. Una diferencia entre ambas es cuando más de un argumento necesita ser evaluado "function(A,B) -&gt ... end." puede tener guardas y valores que coincidan con A y B pero una expresión case necesitaría ser formulada de la siguiente manera:

case {A,B} of
    Pattern Guards -> ...
end.

Esta forma es raramente vista, y puede sorprender un poco al lector. En situaciones similares llamar a una función debería ser más apropiado. De igual manera la función insert/2 previamente escrita, es podría decirse la manera más limpia en lugar de tener una llamada a función para continua con la manejar la simple clausula true o false.

La otra pregunta es para que usar if, dado que cases y functions son los suficientemente flexibles para incluso abarcar if a través de guardas? Lo racional detrás del if es simple. Este fue agregado para que tener en el lenguaje una manera corta de tener guardas sin necesidad de escribir toda la coincidencia de patrones cuando no es necesaria.

[0] http://learnyousomeerlang.com/syntax-in-functions

domingo, 14 de septiembre de 2014

Aprendiendo Erlang parte 6 Modulos III

Más acerca de módulos

Antes de movernos a profundizar nuestro conocimiento en relación a como escribir funciones y algunos fragmentos de código, pero antes tenemos un poco más de información que te será útil en el futuro.

Una de las primeras son los metadatos de los módulos. Los atributos de los módulos son metadatos que describen el módulo en si mismo. Donde podemos encontrar estos metadatos cuando no tenemos acceso al código fuente? Bueno, el compilador nos ayuda con esto: cuando compilamos un módulo, este toma la mayoría de los atributos y los almacena en una función llamada module_info/0 . Así pueden ver los metadatos de un módulo.

9> useless:module_info().
[{exports,[{add,2},
           {hello,0},
           {greet_and_add_two,1},
           {module_info,0},
           {module_info,1}]},
 {imports,[]},
 {attributes,[{vsn,[174839656007867314473085021121413256129]}]},
 {compile,[{options,[]},
           {version,"4.6.2"},
           {time,{2009,9,9,22,15,50}},
           {source,"/home/ferd/learn-you-some-erlang/useless.erl"}]}]
10> useless:module_info(attributes).
[{vsn,[174839656007867314473085021121413256129]}]

El snippet anterior además muestra la función module_info/1 que permite solicitar una pieza especifica de información.

Sintaxis en funciones

Coincidencia de patrones

Ahora tenemos la habilidad de almacenar y compilar código, podemos empezar a escribir funciones más avanzadas. La primera función que vamos a escribir necesita saludar de manera diferente según el genero.  En la mayoría de los lenguajes podrías escribir algo así.

function saludar(Genero, Nombre)
    if Genero == masculino then
        printf("Hola, Sr. %s!", Nombre)
    else if Genero == femenino then
        printf("Hola Sra. %s!", Nombre)
    else
        printf("Hola, %s!", Nombre)

Con coincidencia de patrones (pattern matching), erlang te ayuda a no escribir tanto código similar. La misma función en erlang se vería como esto.

saludar(masculino, Nombre) ->
    io:format("Hola, Sr. ~s!", [Nombre]);
saludar(femenino, Nombre) ->
    io:format("Hola, Sra. ~s!", [Nombre]);
saludar(_, Nombre) ->
    io:format("Hola, ~s!", [Nombre]).

Hay que adminit que la función de impresión por pantalla es un poco más fea en erlang que entro lenguaje pero ese no es el punto. La diferencia principal aquí es que nosotros usamos la coincidencia de patrones para definir las dos partes de una función se debe utilizar y ligas los valores al mismo tiempo. Aquí no se necesita primero ligar los valores y entonces compararlos.
En lugar de:

function(Args)
    if X then
        Expression
    else if Y then
        Expression
    else
        Expression

Nosotros escribimos:

function(X) ->
    Expression;
function(Y) ->
    Expression;
function(_) ->
    Expression.

De esta manera se obtiene los mismo resultados pero con un estilo más declarativo. Cada una de estas declaraciones de funciones es llamada clausula de función. Las clausulas de función deben ser separadas con punto y coma ";"

La coincidencia de patrones en las funciones puede ser más complejo y poderoso que eso. Tal vez recuerdas de capitulos anteriores donde usabamos coincidencia de patrones patrones para encontrar la cabeza y cola de una lista. Vamos a hacer esto.  Creamos un modulo llamado funciones.

-module(functions).
-compile(export_all).

La primera función que vamos a escribir es head/1, que actua exactamente como erlang:hd/1 que toma una lista como argumento y retorna su primer elemento. Los haremos con la ayuda del signo "|".

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

Si vos escribis functions:head([1,2,3,4]) en la terminal (una vez que el modulo sea compilado), puedes esperar que te retorne el valor 1. Consecuentemente para obtener el segundo elemento de la lista, puedes crear la siguiente función.

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

La lista será deconstruida por erlang en orden a hacer coincidir los patrones. Intentalo en la terminal.

1> c(functions).
{ok, functions}
2> functions.head([1,2,3,4]).
1
3> functions.second([1,2,3,4]).
2

esto podría ser repetido en la lista tanto como quitas, pero es impráctico para cientos de valores. Esto se puede resolver escribiendo funciones recursivas, aunque veremos como más adelante. Por ahora concentremonos más en la coincidencia de patrones. El concepto de variables libres y llenas nosotros los discutimos anteriormente, esto es así también para las funciones, podemos entonces comparar y conocer si dos parametros pasados a una función son lo mismo o no. Para esto, crearemos una función llamada same/2 que toma dos argumentos y dice si son identicos.

same(X,X) ->
    true;
save(_,_) ->
    false.

Y es así de simple.

Guardas, guardas.

Las guardas son clausulas adicionales que pueden ir en la cabecera de una función para hacer la coincidencia de patrones más expresiva. Como mencionamos antes la coincidencia de patrones está de alguna manera limitada ya que no puede expresar cosas como rangos de valores o cierto tipo de datos. Unos conceptos que no podemos representar serían los siguientes. Es este jugador de 12 años demasiado petizo para jugar con los profesionales? Es esta distincia demasiado larga para caminar sobre tus manos? Eres demasiado viejo o demasiado joven para manejar un auto?. No puedes responder esto simplemente con coincidencia de patrones. Se puede representar la pregunta sobre el manejo de un auto de la siguiente manera.

old_enough(0) -> false;
old_enough(1) -> false;
old_enough(2) -> false;
...
old_enough(14) -> false;
old_enough(15) -> false;
old_enough(_) -> true.

Pero esto es increiblemente impráctico. Puedes hacerlo si lo quieres, pero trabajarás solo en tu código por siempre. Si quieres eventualmente hacer amigos, entonces debes usar el modulo de guardas  así podremos escribir la pregunta sobre el manejo de la siguiente manera.

old_enough(X) when X >= 16 -> true;
old_enough(_) -> false.

y listo. Como puedes ver es mucho más limpio y corto. Notarás que la regla básica de una guarda es que debe retornar true cuando es correcta, la guarda puede fallar si retorna false o si lanza una excepción. Supongamos ahora que no queremos tener en cuenta a las personas que son mayores de 104 años. Entonces deberiamos cuidarnos de eso, pero como?, simplemente agregando una segunda guarda.

right_age(X) when X >= 16, X <= 104 ->
    true;
right_age(_) ->
    false.

La "," funciona como un "y tambien", y el punto y coma ";"  funciona como un "o sino"


http://learnyousomeerlang.com/modules#mode-about-modules

miércoles, 9 de abril de 2014

Aprendiendo Erlang parte 6 Modulos II

Una última función agregada al modulo, usando ambas funciones anteriores

greet_and_add_two(X) ->
    hello(),
    add(X,2).

No olvides agregar greet_and_add_two/1 a la lista de funciones exportadas. En las llamadas a hello/0 y add/2 no necesitas escribir el nombre del modulo delante de ellos por que son declaradas en el módulo mismo.

Si hubieras querido ser capaz de llamar a io:format/1 en la misma manera que add/2 o cualquier otra función definida en el módulo, deberías agregar el siguiente atributo de modulo al comienzo del archivo -import(io, [format/1]). Entonces podrías llamar a format('Hola Mundo!~n'). directamente. De manera más general puede seguir esta receta.

-import(Module, [Funcion1/Aridad, ..., FuncionN/Aridad]).

Importar una función no es más que un atajo para los programadores cuando escriben su código. Los programadores Erlang a menudo desalientan el uso del atributo -import ya que algunas personas encuentran que reduce la legibilidad del código. En el caso de io:format/2 la función io_lib:format/2 también existe. En caso de que se use una de estas el programador tendría que ir al comienzo del archivo para saber de cual de las dos se trata. Consecuentemente, dejar el nombre de módulo es considerada una buena práctica. Usualmente, las únicas funciones que verás importadas vienen del módulo de listas: estas funciones son usadas con mucha frecuencia  que las otros módulos.

Tu módulo useless debería ahora verse algo así

-module(useless).
-export([add/2, hello/0, greet_and_add_two/1]).

add(A,B) ->
    A + B.

%%
%%
hello() ->
    io:format("Hola mundo!~n").

greet_and_add_two(X) ->
    hello(),
    add(X,2).

Hemos terminado con el módulo 'useless'. Puedes guardar el archivo bajo el nombre userless.erl . El nombres del archivo deberá el nombre del módulo como fue definido en el atributo -module, seguido de '.erl' que es el tipo de extensión standard para Erlang.

Anteriormente vimos como compilar el módulo y finalmente intentar todas sus funciones, veremos como definir y usar macros. Las macros de Erlang son realmente similares a las declaraciones '#define' de C, principalmente usado para definir funciones cortas y constantes. Ellas son expresiones simples representadas por texto que será reemplazado antes de que el código sea compilado por la VM. Tales macros son útiles principalmente para evitar valores mágicos flotando alrededor de tus módulos. Una macro es definida como un atribudo módulo de la forma -define(MACRO, some_value).  y es usada como ?MACRO dentro de cualquier función definida en el módulo. Una 'función' macro debería escribirse como -define(sub(X,Y), X-Y). y usada como ?sub(23,47) luego reemplazado por 23-47 por el compilador. Algunas personas usarán macros más complejas, pero la sintaxis básica se mantiene igual.

Compilando el código

El código Erlang es compilado a bytecode para ser usado por la máquina virtual. Puedes llamar al compilador desde distintos lugares $ erlc flags file.erl en la linea de comandos, compile:file(FileName) en la shell o en un módulo c() en la shell, etc.

Es tiempo de compilar nuestro módulo useless. Abrir la Shell de Erlang y escribir lo siguiente.

1> cd("/path/to/where/you/saved/the-module").
"Path name to the directory you are in"
ok

De manera predeterminada, la shell solo busca archivos en el mismo directorio que esté fue lanzado y en la libreria estandar: cd/1 es una función definida exclusivamente para la Shell de Erlang, diciendole que cambie el directorio uno nuevo por lo que es menos molesto navegar por nuestros archivos. Los usuarios de Windows deberían recordar de usar la barra invertida. Cuando esto se realice haz lo siguiente.

2> c(useless).
{ok, useless}

Si obtienes otro mensaje, asegurate que el nombre del archivo es correcto, que estás en el directorio correcto, y que no tienes errores en tu módulo. Una vez que compiles el código exitosamente, te darás cuenta que un archivo useless.beam fue agregado en el mismo directorio que tu useless.erl. Este es el módulo compilado. Probemos nuestras funciones.

3> useless:add(7,2).
9
4> useless:hello().
Hello, World!
ok
5> useless:greet_and_add_two(-3).
Hello, World!
-1
6> useless:not_a_real_function().
** exception error: undefined function useless:not_a_real_function/0

Las funciones funcionando como esperamos add/2 agrega números, hello/0 muestra por pantalla "Hello World!", y greet_and_add_two/1 hace ambas cosas. Por supuesto, te preguntarás por que hello/0 retorna el atomo ok luego del texto saliente. Esto es por que las funciones Erlang y expresiones deben siempre returnar algo, incluso si ellas no son necesarias en otros lenguajes. Así como, io:format/1 retorna 'ok' para denotar una condición normal, la ausencia de errores.

La expresión 6 muestra un error siendo lanzado por que una función no existe. Si te olvidás de expotar una función, este es el tipo de mensaje de error que obtendrás cuando lo ejecutes.

Hay un montón de banderas de compilación existentes para tenes más control sobre como es compilado el módulo.  Puedes obtener una lista de todos ellos en la documentación de Erlang [0] . Los más comunes son.

-debug_info
Las herramientas de Erlang como debuggers, cobertura de código, y herramientas de analisis estático se usan para la información de depuración de un módulo con el fin de realizar su trabajo.

-{outdir, Dir}
Por default, el compilador de Erlang creará los archivos 'beam' en el directorio actual. Este te permitirá elegir donde poner el archivo compilado.

-export_all
Ignorará el atributo -export del módulo y en su lugar exportará todas las funciones definidas. Este es principalmente útil cuando estás probando o desarrollando código nuevo, pero no debería ser usado en producción.

-{d, Macro} or {d,Macro,Value}
Define una macro a ser usada en el módulo, donde Macro es un atomo. Este es usado más frecuentemente cuando se trata de pruebas unitarias, lo que garantiza que un módulo solo tendrá sus funciones de prueba creadas y exportadas cuando se quieren explicitamente. Por default, Value es 'true' si esta no es definida en el tercer lugar de la tupla.

Para compilar nuestro módulo useless con algunas banderas, deberíamos hacer lo siguiente:

7> compile:file(useless, [debug_info, export_all]).
{ok, useless}
8> c(useless, [debug_info, export_all]).
{ok, useless}

Puedes tambien ser astuto y definir opciones del compilador dentro de un módulo con un atributo de módulo:

-compile([debug_info, export_all]).

Entonces solo compilará y obtendrás los mismos resultados que si pasaras las banderas manualmente. Ahora estamos listos para escribir funciones, compilarlas, y ejecutarlas. Es el momento de ver hasta donde podemos llevarlo.

Tarde y de a poco sigo traduciendo, como salga, pero aprendiendo algo cada día. [1]
[0] http://erlang.org/doc/man/compile.html
[1] http://learnyousomeerlang.com/modules

lunes, 3 de marzo de 2014

Aprendiendo Erlang parte 6 Modulos I

Los módulos son un grupo de funciones reagrupadas en un solo archivo, bajo un nombre. Adicionalmente todas las funciones en erlang deben ser definidas en módulos. Ya has usado modulas tal vez sin darte cuenta. Las BIFs (Built-In Functions) mencionadas en el capitulo anterior hd o tl, en realidad pertenecen al módulo erlang, así como toda la aritmética. lógica u operadores booleanos. Los BIFs del módulo erlang difieren de otros módulos en que son automaticamente importados cuando usas Erlang. Cada una de las otras funciones definidas en un modulo que uses debe ser llamada con la forma Modulo:Funcion(Argumentos).

Lo podés ver por vos mismo:

1> erlang:element(2, {a,b,c}).
b
2> element(2,{a,b,c}).
b
3> lists:seq(1,4).
[1,2,3,4]
4> seq(1,4).
** exception error: undefined shell command seq/2

Aquí la función seq  del modulo lists no fue automáticamente importada mientras que element si lo fue. El error 'undefined shell command' viene desde la shell buscando por un comando shell como f() y no es capaz de encontrarlo. Hay algunas funciones del modulo erlang que no se importan automáticamente, pero tampoco son muy usadas.

Lógicamente, deberías ponet funciones similares dentro de un mismo modulo. Las operaciones comunes sobre listas son mantenidas dentro del modulo lists, mientras que las funciones de entrada y salida (como escribir en una terminal o en un archivo) son agrupadas en el modulo io. Uno de los únicos módulos que no respeta ese patrón es el antes mencionado modulo erlang que tiene funciones de matemática, conversiones, trabajar con multiprocesamiento, o jugar con la configuración de la máquina virtual, etc. Ellos no tienen ningún punto en común solo que son funciones previamente construidas (BIFs). Tu deberías evitar crear módulos como erlang y en su lugar enfocarse en separaciones lógicas limpias.

Declaraciones de Módulos

Cuando escribimos módulos, tu puedes declarar dos tipos de cosas: funciones y atributos. Los atributos son metadatos que describen el módulo en si mismo como es su nombre, las funciones que deberían ser visibles a el mundo exterior, el autor del código y así sucesivamente. Este tipo de metadatos es útil porque le dá indicaciones al compilador sobre como debe hacer su trabajo y también por que este permite a la gente obtener información útil sobre el código compilado sin tener que consultar las fuentes.

Hay una gran variedad de atributos de módulos actualmente usados en el código de Erlang a través del mundo, de hecho, podes incluso declarar tus propios atributos para lo que quieras. Hay algunos atributos predefinidos que aparecerán más frecuentemente que otros en tu código. Todos los atributos de módulo siguen la forma de -Nombre(Atributo).. Solo uno de ellos es necesario para que tu módulo sea compilable.

-module(Nombre).
Este es siempre el primer atributo (y declaración) de un archivo y por una buena razón: este es el nombre del módulo actual, donde el nombre es un atomo. Este es el nombre que usarás para llamar a funciones de otros módulos. Las llamadas son hechas con la forma M:F(A) donde M es el nombre del módulo, F la función, y A los argumentos.
Es hora de escribir código! Nuestro primer módulo será muy simple y poco útil. Abrí tu editor de texto y escribe lo siguiente, y guárdalo baje el nombre userless.erl

-module(useless).

Esta linea de texto es un módulo válido. Realmente! Por supuesto que esto es muy poco útil. Primero vamos a decidir que funciones vamos a exportar de nuestro módulo 'useless'. Para hacer esto, usaremos otro atributo.

-export([Funcion1/Aridad, Funcion2/Aridad, ..., FuncionN/Aridad]).Este es usado para definir que funciones de un módulo puede ser llamadas  desde el mundo exterior. Este toma una lista de funciones con su respectiva aridad. La aridad de una función es un entero que representa cuantos argumentos pueden ser pasados a la función. Esta es una información critica, por que diferentes funciones definidas dentro de un módulo pueden compartir el mismo nombre si y solo sí ellas tienen aridad diferente. Las funciones add(X,Y) y add(X,Y,Z), por lo tanto deberían ser consideradas diferentes y escritas en la forma add/2 y add/3 respectivamente.
Nuestro modulo useless exportará una función útil llamada 'add' que tomará dos argumentos. El siguiente atributo -export puede ser agregado después de la declaración del módulo.

-export([add/2]).

y ahora escribir la función

add(A,B) ->
    A + B.

La sintaxis de una función sigue la forma Nombre(Argumentos) -> Cuerpo. donde el Nombre tiene que ser un átomo y el Cuerpo puede ser uno o más Expresiones Erlang separada por comas.La función finaliza con un punto. Nota que erlang no utiliza la palabra clave 'return' . 'Return' es inutil. En su lugar, la última expresión lógica de una función a ejecutar tendrá su valor retornado al llamador automaticamente sin tener que mencionarlo.

Agrega la siguiente función (por que sí, todo tutorial necesita su ejemplo "Hola Mundo" Incluso en su cuarto cápitulo!)  sin olvidar agregar el atributo -export

%% Mostrar Saludo.
%% io:format/1 es una función estándar para salida de texto.
hello() ->
    io:format("Hola Mundo!~n").

Lo que se observa en esta función es que los comentarios son de una sola línea y comienzan con un signo % (se utiliza %% simplemente como una cuestión de estilo). La función  hello/0 demuestra como llamar funciones de otros módulos dentro de tu módulo. En este caso io:format/1 es la función estándar para escribir texto en pantalla como es escrito en el comentario.

[0] http://es.wikipedia.org/wiki/Aridad

viernes, 7 de febrero de 2014

Aprendiendo Erlang parte 5 Listas por comprensión

Las listas por comprensión son maneras para construir y modificar listas. Ellos hacen que los programas sean cortos y fáciles de entender comparada a otras maneras de manipular listas. Que está basado en la idea de notación de conjuntos; Si alguna vez has tomado clases de matemáticas con la teoría de conjuntos o si alguna vez has mirado la notación matemática, tal vez conozcas como funciona esto. La notación de conjuntos básicamente te dice cómo construir un conjunto especificando las propiedades que sus miembros deben satisfacer. Las listas por comprensión son difíciles de entender en principio, pero vale la pena el esfuerzo. Ellas hacen el código limpio y corto, así que no dudes en intentarlo y escribir ejemplos hasta lo que entiendas.

Un ejemplo de notacion de conjuntos sería . Esa notación de conjuntos te dice que el resultado que buscas debería ser todos los números reales los cuales son igual a su propio cuadrado.El resultado de aquel conjunto debería ser {0,1}. Otra ejemplo de notación de conjuntos, más simple y abreviado sería {x : x > 0}. Aquí, buscaremos todos los números donde x > 0.

1> [2*N || N <- [1,2,3,4]]
[2,4,6,8]

Comparando con la notación mátematica a la de Erlang no hay tantos cambios. las llaves ({}) se convierten en corchetes ([]), los dos puntos (:) se convierten en dos pipes (||) y la palabra "en" se convierte en una flecha (<-) solamente cambiamos signos  y mantenemos la misma lógica. En el ejemplo anterior, cada valor de [1,2,3,4] secuencialmente coincide con el patrón N. La flecha actua exactamente como operador el "=", con la excepción que no lo hace lanzar excepciones.

También puede agregarle restricciones a la lista de comprensión usando operaciones que retornen valores booleanos. Si nosotros buscamos que todos los numeros seán pares sobre 10 de ellos, podríamos escribir algo como esto.

2> || X <- [1,2,3,4,5,6,7,8,9,10], X rem 2 =:= 0].
[2,4,6,8,10]

Donde X rem 2 =:= 0 chequea si un número es par. Las aplicaciones prácticas vienen cuando decidimos aplicar una función a cada elemento de una lista, forzando a respetar restricciones, etc. Como un ejemplo, digamos que tenemos un restaurante. Un cliente entra, ve nuestro menú y pregunta si el podría obtener los precios de todos los items que cuestan entre $3 a $10 con impuestos (digamos 7%).

3> RestaurantMenu = [{steak, 5.99}, {beer, 3.99}, {poutine, 3.50}, {kitten, 20.99}, {water, 0.00}].
[{steak,5.99},
 {beer,3.99},
 {poutine,3.5},
 {kitten,20.99},
 {water,0.0}]
4> [{Item, Price*1.07} || {Item, Price} <- RestaurantMenu, Price >= 3, Price =< 10].
[{steak,6.409300000000001},{beer,4.2693},{poutine,3.745}]

Por supuesto, los decimales no están redondeados en una manera legible, pero creo que lo entiendes. La receta para las listas de comprensión en Erlang es por lo tanto NuevaLista = [Expresion || Patron <- Lista, Condicion1, Condicion2, ... CondicionN]. La parte Patron <- Lista es llamada expresión Generador. Puedes tener más de uno.

5> [X+Y || X <- [1,2], Y <- [2,3]].
[3,4,4,5]

Esto corre las operaciones 1+2, 1+3, 2+2, 2+3. Asi que si quieres hacer la receta de las listas por comprensión más generica podrías obtener: NuevaLista = [Expresion || GeneradorExp1, GeneradorExp2, ..., GeneradorExpN, Condicion1, Condicion2, ..., CondicionM] . Nota que las expresiones generador son emparejadas con la coincidencia de patrones que funciona como filtro.

6> Clima = [{la_pampa, lluvioso}, {buenos_aires, tormenta}, {cordoba, niebla}, {tucuman, soleado}, {entre_rios, niebla}, {santa_cruz, nevado}].                                                                                           
[{la_pampa,lluvioso},
 {buenos_aires,tormenta},
 {cordoba,niebla},
 {tucuman,soleado},
 {entre_rios,niebla},
 {santa_cruz,nevado}]
7> LugaresConNiebla = [X || {X, niebla} <- Clima].
[cordoba,entre_rios]

Si un elemento en la lista Clima no coincide con el patrón {X, niebla}, este es simplemente ignorado en la lista de comprensión donde en una igualdad lanzaría una excepción.

Hay otro tipos de datos básicos que nos queda por ver, por ahora. Esta es una carácteristica sorprendente que hace fácil interpretar datos binarios.

martes, 28 de enero de 2014

Aprendiendo Erlang parte 4 listas

Listas

Las listas son el pan y la manteca de muchos lenguajes funcionales. Estos son usados para resolver todo tipo de problemas y es sin dudas la estructura de datos más usada en Erlang. Las listas puede contener cualquier cosa! Números, átomos, tuplas, otras listas; tus sueños más salvajes en un estructura simple. La notación básica de un lista es [Elemento1, Elemento2, ... ElementoN] y puedes mezclar más que un tipo de dato en él:

1> [1,2,3, {numbers, [4,5,6]}, 5.34, atom].
[1,2,3,{numbers,[4,5,6]},5.34,atom]

Bastante simple, verdad?

2> [97,98,99].
"abc"

Que pasó acá??? Esta es una de las cosas que menos gustan en Erlang. Cadenas de texto, las cadenas de texto son listas y la notación es absolutamente la misma! Por que a la gente no le gusta? Porque?

3> [97,98,99,4,5,6].
[97,98,99,4,5,6]
4> [233].
"é"

Erlang imprimirá listas de números como números solo cuando al menos uno de ellos no represente una letra! No hay tal cosa como una cadena de texto real en Erlang! Esto va sin dudas a obsersionarte en el futuro y odiarás el lenguaje por esto. No desesperes, por que hay otras maneras de escribir cadenas de texto nosotros lo veremos más tarde en este cápitulo.

Para pegar listas juntas, nosotros usamos el operador ++. Lo contrario de ++ es -- y podremos remover elementos de una lista:

5> [1,2,3] ++ [4,5].
[1,2,3,4,5]
6> [1,2,3,4,5] -- [1,2,3].
[4,5]
7> [2,4,2] -- [2,4].
[2]
8>
8> [2,4,2] -- [2,4,2].
[]

Ambos ++ y -- son asociados desde la derecha. Esto significa que los elementos de muchos operaciones -- o ++ deben ser realizados desde la derecha hacia la izquierda, como en los siguientes ejemplos:

9> [1,2,3] -- [1,2] -- [3].
[3]
10> [1,2,3] -- [1,2] -- [2].
[2,3]

Vamos a seguir adelante. El primer elemento de una lista es llamado Cabeza(Head), y el resto de la lista es llamado la Cola(Tail). Usaremos dos funciones incorporados (Build-In Functions BIF) para conseguirlos.

11> hd([1,2,3,4]).
1
12> tl([1,2,3,4]).
[2,3,4]

Accesando o agregando la cabeza es rápido y eficiente: virtualmente todas las aplicaciones donde necesites operar con listas siempre operas en la cabeza primero. Como se usa con tanta frecuencia, hay una mejor manera de separar la cabeza de la cola de una lista con la ayuda de la coincidencia de patrones: [Head, Tail]. 
Así es como deberías agregar una nueva cabeza a una lista.

13> List = [2,3,4].
[2,3,4]
14> NewList = [1|List].
[1,2,3,4]

Cuando procesamos listas, tu usualmente empiezas con la cabeza, y buscas una manera rápida de guardar la cola para más tarde operar sobre ella. Si recuerdas la manera en que trabajan las tuplas y como nosotros usamos la coincidencia de patrones para desempaquetar valores de un punto ({X,Y}), entonces sabrás que podemos obtener el primer elemento (la cabeza) cortando la lista de una manera similar.

15> [Head|Tail] = NewList.
[1,2,3,4]
16> Head.
1
17> Tail.
[2,3,4]
18> [NewHead|NewTail] = Tail.
[2,3,4]
19> NewHead.
2

El | que nosotros usamos es llamado el operador cons (constructor). De hecho, cualquier lista puede ser construida con solo valores y cons.

20> [1 | []].
[1]
21> [2|[1 | []]].
[2,1]
22> [3 | [2 | [1 | []]]].
[3,2,1]

Esto quiere decir que cualquier lista puede ser creada siguiendo la formula [Term1 | [Term2 | [... | [ TermN ] ] ] ] ... Las listas pueden de este modo ser definidas de manera recursivamente como una cabeza precedida de una cola, que en si misma es una cabeza seguida de más cabezas. En este sentido nosotros podriamos imaginar una lista siendo un poco como un gusano tu podrías cortarlo en la mitad y entonces obtendrías dos gusanos.



viernes, 24 de enero de 2014

Aprendiendo Erlang parte 3 (tuplas)

Tuplas

Una tupla es una manera de organizar los datos. Esta es una manera de agrupar juntos muchos términos cuando se sabe cuantos hay. En Erlang, una tupla es escrita de la siguiente forma {Element1, Element2, ..., ElementN}. Como un ejemplo, obtenemos las coordenadas (x,y) si buscas la posición de un punto Catersiano en un gráfico. Nosotros podemos representar este punto como una tupla de dos términos:

1> X = 10, Y=4.
4
2> Point = {X,Y}.
{10,4}

En este caso, un punto será siempre de dos términos. En lugar de llevar las variables X e Y a todos lados, tu solo debes llevar una en su lugar. Sin embargo, que puedes hacer tu si recibes un punto y solo buscas la coordenada X? No es difícil extraer esa información. Recuerda que cuando nosotros asignemos valores, Erlang nunca se quejaría si ellos fueran los mismos. Vamos a explotar esto. Necesitarías limpiar las variables que nosotros habíamos asignado.

3> Point = {4,5}.
{4,5}
4> {X,Y} = Point.
{4,5}
5> X.
4
6> {X,_} = Point.
{4,5}

A partir de ahora podemos usar X para obtener el primer valor de la tupla! Como sucedió esto? Primero, X e Y no tenía ningún valor y por lo tanto se considerarán las variables no ligadas. Cuando les propusimos en la tupla {X,Y} a la izquierda del operador =, el operador = comparó ambos valores {X,Y} vs {4,5}. Erlang es lo suficientemente inteligente para desempaquetar los valores de una tupla y distribuirlos en las variables vacias en el costado izquierdo. Entonces la comparación es solo {4,5} = {4,5}, que obviamente es lo que sucede. Esta es una de las muchas formas de la coincidencia de patrones.

Nota que en la expresión 6, yo usé la variable anónima _. As es exactamente como debería ser usada: para descartar un valor que usualmente está en un lugar pero que no vamos a usar. La variable _ es siempre observada como vacia y actua como comodín para la coincidencia de patrones. La coincidencia de patrones para desempaquetar tuplas solo funciona si el número de elementos (longitud de la tupla) es el mismo.

7> {_,_} = {4,5}.
{4,5}
8> {_,_} = {4,5,6}
** exception error: no match of right hand side value {4,5,6}

Las tuplas son útiles cuando trabajamos con valores simples. Como es eso? El ejemplo más simple es la temperatura.

9> Temperatura = 23.213.
23.123

Bueno, esto suena bien para un buen dia en la playa... espera, esta temperatura es en Kelvin, Celcius o Fahrenheit?

10> TemperaturaPrecisa = {celsius, Temperatura}.
{celsius, 23.213}
11> {kelvin, T} = TemperaturaPrecisa
** exception error: no match of right hand side value {celsius, 23.123}

Esto lanza un error, pero esto es exactamente lo que buscamos. Esto es de vuelta la coincidencia de patrones trabajando. El operador = termina comparando {kelvin, T} and {celsius, 23.213}: incluso si la variable T está vacía, Erlang no observa que el átomo celsius sea identico al átomo kelvin cuando se comparan. Una excepción es lanzada que para la ejecución del código. Al hacerlo, la parte de nuestro programa que espera una temperatura en Kelvin no será capaz de procesar la temperatura enviada en Celsius. Esto hace fácil para el programador que está siendo enviado y también trabaja como una ayuda para el debugging. Una tupla que contiene un atomo con un elemento es llamada una "tupla  etiquetada". Cualquier elemento de una tupla puede ser cualquier elemento incluso una tupla.

12> {point, {X,Y}.
{point, {4,5}}

Que pasaría si quisieras contener más de un punto?

Se vienen las listas!

viernes, 17 de enero de 2014

Erlang Ejercicios

Una vez leída gran parte del libro Erlang/OTP [0] de Manuel Rubio y haber hecho una muy pequeña parte de la traducción de "learn you some erlang" [1] para mi Blog que luego voy a poner la parte 3.

Me puse a experimentar un poco e instalar por ejemplo ChicagoBoss [2] que es un Framework para hacer aplicaciones Web con Erlang, es muy sencillo, me gustó mucho y luego voy a hacer un post sobre lo que aprendí de su instalación y su funcionamiento,

Bueno finalmente les voy a traducir los ejercicios [3] que hice para probar lo poco que fui aprendiendo y si quieren van a poder ver el código fuente en github [4]

Entrando a un programa

Escribe el ejemplo demo:double en un archivo llamado demo.erl. Usa tu editor de texto preferido.
Empezar con Erlang.

Escribe el comando c:c(demo) a compilar el archivo.

Intenta correr la consulta:

 demo:double(12).
Esto es solo una prueba si estás empezando con el sistema y usar el editor junto con el sistema Erlang.

Programas secuenciales simples


1. Escribe las funciones temp:f2c(F) y temp:c2f(C) que convierte entre las escalas de Grados Centigrados  y Fahrenheit (ayuda 5(F-32) = 9C  ).

2. Escribe una función temp:convert(Temperatura) que combine la funcionalidad de f2c y c2f. Ejemplo.


 > temp:convert({c,100}).
 => {f,212}
 > temp:convert({f,32}).
 => {c,0}
3. Escribe una función mathStuff:perimeter(Form) que nos informe el perimetro de diferentes formas. Form puede ser una de las siguientes.

{square,Side}
{circle,Radius}
{triangle,A,B,C}
En el github está además el ejercicio de programas recursivos que "no me salió y tuve que buscarlo para aprender a hacerlo" pero bueno capaz les sirva.

[0] http://erlang.bosqueviejo.net/
[1] http://learnyousomeerlang.com
[2] http://www.chicagoboss.org/
[3] http://www.erlang.org/course/exercises.html
[4] https://github.com/gonzafirewall/ejercicios-erlang

jueves, 9 de enero de 2014

Aprendiendo Erlang parte 2

Las variables invariables

Algo que no dije en el post anterior pero lo habrán visto es que las "variables invariables" tienen que ser escritas con su primer letra en mayúscula. Sino me creén intentenlo

 admin@kaizen:~$ erl  
 Erlang R16B01 (erts-5.10.2) [source] [64-bit] [async-threads:10] [kernel-poll:false]  
 Eshell V5.10.2 (abort with ^G)  
 1> Var = 'Este es un texto loco'.  
 'Este es un texto loco'  
 2> Var.  
 'Este es un texto loco'  
 3> var = 'Esta se va a romper'.  
 ** exception error: no match of right hand side value 'Esta se va a romper'  

Átomos - la razón de la primer letra en mayúscula

Los atomos son literales [0] con su propio nombre como valor. Como dice en la parte del libro que ya esta traducida [1] el átomo perro significa 'perro' y nada más que eso. No lo podeś cambiar, no lo podés cortar en pedazos. Los átomos son comparables a las constantes. Pero en este caso es útil por que no son definidas en si misma por consiguiente nunca te va a pasar que una constante no esté definida.

Worry you or won't be happy (Preocúpate o no serás feliz)

No bebas demasiado Kool-Aid3 Los átomos son realmente buenos para enviar mensajes o representar constantes. Sin embargo, te encontraras con problemas al utilizar los átomos: Un átomo es referenciado4 en una "tabla de atomos" la que consume memoría (4 bytes por átomo en un sistema de 32-bits y 8 bytes por átomo en un sistema de 64 bits). Sobre la tabla de átomos no actúa el recolector de basura (garbage collector) y por lo tanto los átomos se acumularan hasta que el sistema se caiga por uso de memoria o porque 1048577 átomos fueron declarados.

Nota: algunos átomos son palabras reservadas y no puede ser usadas excepto para lo que diseñadores del lenguajes querrían que fueran: nombres de funciones, operadores, expresiones: etc. Estos son: after and andalso band begin bnot bor bsl bsr bxor case catch cond div end fun if let not of or orelse query receive rem try when xor


Álgebra de Boole y Operadores de comparación

En erlang la algebra booleana funciona como es de esperar pero tiene algunas cosas puntuales a las que hay que prestar atención.

 1> true and false.  
 false  
 2> false or true.  
 true  
 3> true xor false.  
 true  
 4> not false.  
 true  
 5> not(true and true).  
 false  

Hasta acá creo que todo bonito bueno acá vamos de acá en adelante vamos a encontrar una primera diferencia en cuanto a comparaciones que estamos acostumbrados.

 6> 5 =:= 5.  
 true  
 7> 1 =:= 0.  
 false  
 8> 1 =/= 0.  
 true  
 9> 5 =:= 5.0.  
 false  
 10> 5 == 5.0.  
 true  
 11> 5 /= 5.0.  
 false  

La igualdad total incluso de tipo es "=:=" como vemos en "5 =:= 5.0" nos informa "false" y la desigualdad sería "=/=" también existe una comparación un poco más permisiva que es la que sería "==" o la desigualdad "/="

Luego tenemos los comparadores de mayor o menor etc.

 12> 1 < 2.  
 true  
 13> 1 < 1.  
 false  
 14> 1 >= 1.  
 true  
 15> 1 =< 1.  
 true  

Según la traducción y yo concuerdo la última linea parece estar al revés pero bueno alguno diría "no es un bug es una feature" (traducible aprox a. "no es un problema es una característica")

Otra cuestión a tener en cuenta es. Que debería devolver esta comparación 0 =:= false?
.....
....
...

Creo que muchos diriamos que true bueno al menos Python dice eso

>>> 0 == False
True

pero erlang dice que

22> 0 =:= false.
false

No y esto es así por que en erlang no existe el concepto de tipo booleano para true o false sino que son atomos. Pero se integran muy bien en el lenguaje y generalmente nos vas a tener problemas. 

[3]Nota: El orden correcto de cada elemento de la comparación es la siguiente:
number < atom < reference < fun < port < pid < tuple < list < bit string 

En Resumen

=:=    flotantes y enteros no son iguales                           5 =:= 5.0      false
=/=    flotantes y enteros son definitivamente diferentes  5 =/= 5.0       true
==     flotantes y enteros  son lo mismo                           5 == 5.0        true
/=      flotantes y enteros no son diferentes                       5 /= 5.0         false
>       mayor                                                                      10 > 5          true
<       menor                                                                      10 < 5          false
>=     mayor o igual                                                           5 >=  5        true
=<     menor o igual                                                           4 =<  5        true


Vamos a dejar por aquí en la próx vamos a ver otros tipos de datos. Que no están traducidos en la Web de Erlang ARG así que posiblemente aproveche para dar un primer aporte en ese sentido.

[0] http://en.wikipedia.org/wiki/Literal_%28computer_science%29
[1] http://erlang.org.ar/AEComenzandoRealmente
[2] http://es.wikipedia.org/wiki/Recolector_de_basura
[3] http://erlang.org.ar/AEComenzandoRealmente - Algebra de Boole Nota nro 2

viernes, 3 de enero de 2014

Aprendiendo Erlang



Con mis ansias de aprender un lenguaje de programación bastante diferente a Python o que haga pie en un paradigma diferente es que siguiendo la recomendación de marianoguerra [0] que alguna vez [1] me comentó y me recomendó la comunidad de Erlang Argentina [2], es que ahora estoy aprendiendo Erlang. Acá va la definición de Wikipedia!
Erlang es un lenguaje de programación concurrente y un sistema de ejecución que incluye una máquina virtual y biblioteca.

El subconjunto de programación secuencial de Erlang es un lenguaje funcional, con evaluación estricta, asignación única, y tipado dinámico. Fue diseñado en la compañía Ericsson para realizar aplicaciones distribuidas, tolerantes a fallos, soft-real-time y de funcionamiento ininterrumpido. Proporciona el cambio en caliente de código de forma que éste se puede cambiar sin parar el sistema. Originalmente, Erlang era un lenguaje propietario de Ericsson, pero fue cedido como software de código abierto en 1998. La implementación de Ericsson es, principalmente interpretada pero también incluye un compilador HiPE (sólo soportado en algunas plataformas).
Instalando Erlang en Ubuntu

Instalarlo en Ubuntu no lleva mucha magía simplemente haciendo

sudo apt-get -y install erlang

En ubuntu abrímos una Shell Interactiva de Erlang escribiendo
 admin@kaizen-pc:/$ erl   
 Erlang R16B01 (erts-5.10.2) [source] [64-bit] [async-threads:10] [kernel-poll:false]   
 Eshell V5.10.2 (abort with ^G)   
 1> 1 + 1.   
 2   
 2> 400 / 2.   
 200.0   
 3> (100 * 2) / 2.   
 100.0   

Despues de probar unas cuestiones básicas de aritmetica y siguiendo la introducción que está traducida nos damos cuenta que las variables no se pueden variar.
cambiar el valor de cualquier variable esta estrictamente prohibido [3] [4]

 4> One = 1.  
 1  
 5> One = 2.  
 ** exception error: no match of right hand side value 2  

Esto es debido a que un lenguaje puramente funcional solamente acepta la asignación única (Single assignment). [5]
Prestar atención en la shell que debemos terminar las instrucciones con un ".". La cuestión de las variables puede parecer bastante extraño para quienes usamos otro tipo de lenguajes como Python o etc. El tema es que si lo ves desde el lado de la matemática tiene mucho sentido.

 y = 2  
 x = y + 3  
 x = 2 + 3  
 x = 5  
[6]

Otro término que puede sonar extraño pero a la vez una vez entendido tiene bastante lógica es la Transparencia Referencial [7]

Que es por el mismo motivo que no se puede hacer asignación multiple ya que si uno dice que

 x = agregar_dos_a(3) = 5   
 # ya que el resultado de agregarle 2 a 3 siempre es 5  

Pero en casos como la fecha de hoy.

 x = today() = 2009/10/22  
  -- espera un dia --  
 x = today() = 2009/10/23  
 x = x  
 ∴ 2009/10/22 = 2009/10/23  

En Erlang se opta por la practicidad por sobre la teoria cuando es necesario.

Erlang además de ser un lenguaje de programación funcional es además concurrente. Para esto usa el modelo de actor [8] del que posiblemente haga un Post en otro momento. Pero para resumirlo un actor es un proceso separado en la máquina virtual. Para hacer un paralelismo con la vida real sería algo así como una persona encerrada en una oficina esperando que le lleguen peticiones para responderlas y nada más que eso. Imagínense entonces muchas de estas personas esperando las peticiones y respondiendolas según corresponda o ignorándolas. Por consiguiente podrías pedirle a muchas personas que hagan pequeñas tareas y las acciones de cada uno no afectarían a los demás.

Creo que vamos a dejarlo por acá por el momento hay mucho para leer con respecto a la invariabilidad de las variables, la transparencia referencial y el modelo de actor.  Seguramente en el próximo veremos un poco más sobre algunos tipos de variables que se pueden usar, etc. Capaz un "hello world" no sé veremos. Hay todo un año todavia!!!

[0] http://marianoguerra.org/
[1] http://blog.deploshark.com.ar/2013/10/tacticas-de-disponibilidad-recuperacion.html
[2] http://erlang.org.ar/
[3] http://erlang.org.ar/AEIntroduccion
[4] http://erlang.org.ar/AEComenzandoRealmente - Variables Invariables
[5] http://en.wikipedia.org/wiki/Single_assignment#Single_assignment
[6] http://erlang.org.ar/AEIntroduccion - Entonces que es Erlang?
[7] http://es.wikipedia.org/wiki/Transparencia_referencial
[8] http://en.wikipedia.org/wiki/Actor_model