Programación en Erlang

De Wikilibros, la colección de libros de texto de contenido libre.
Saltar a: navegación, buscar
Programación en Erlang

Introducción[editar]

Erlang es un lenguaje de programación concurrente y un sistema de ejecución que incluye una máquina virtual y bibliotecas. 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 open source en 1998. La implementación de Ericsson es, principalmente interpretada pero también incluye un compilador HiPE (sólo soportado en algunas plataformas). La creación y gestión de procesos es trivial en Erlang, mientras que, en muchos lenguajes, los hilos se consideran un apartado complicado y propenso a errores. En Erlang toda concurrencia es explícita.

Contenidos

Historia[editar]

Información General[editar]

Erlang fue diseñado para escribir programas concurrentes que se ejecutasen eternamente. Erlang usa procesos concurrentes para estructurar el programa. Estos procesos no tienen memoria compartida y se comunican por paso de mensajes asíncronos. Los procesos de Erlang son ligeros y pertenecen al lenguaje, no al sistema operativo. Erlang tiene mecanismos que permiten que los programas cambien on-the-fly (en vivo) así, esos programas pueden evolucionar y cambiar sin detener su ejecución. Estos mecanismos simplifican la construcción de software implementando sistemas non-stop (que no se detienen). El desarrollo inicial de Erlang tuvo lugar en 1986 en el Laboratorio de Computación de Ericsson. Erlang fue diseñado con un objetivo específico en mente: proporcionar una mejor forma de programar aplicaciones de telefonía. En ese momento, las aplicaciones de telefonía eran atípicas del tipo de problemas que podían resolver los lenguajes de programación convencionales. Las aplicaciones de telefonía son, por su naturaleza, altamente concurrentes: un simple switch debe manejar decenas o cientos de miles de transacciones simultáneas. Tales transacciones son intrínsecamente distribuidas y el software se espera que sea altamente tolerante a fallos. Cuando el software que controla los teléfonos falla, sale en los periódicos, algo que no ocurre cuando fallan las aplicaciones de escritorio. El software de telefonía debe también cambiar on-the-fly, esto es, sin perder el servicio mientras se realiza una actualización del código. El software de telefonía debe también operar en tiempo real, con ajustados requisitos de tiempo para algunas operaciones, y más relajado tiempo en otras clases de operaciones.

Datos[editar]

Creador: Joe Armstrong

Empresa Desarrolladora: Ericsson

Año de Creación: 1986

Objetivo: Solucionar los problemas de un entorno altamente concurrente, que no puede permitirse caer y que debe de actualizarse sin pérdida de servicio.

Razón de su Nombre: Erlang recibe el nombre de A. K. Erlang. A veces se piensa que el nombre es una abreviación de ERicsson LANGuage, debido a su uso intensivo en Ericsson. Según Bjarne Däcker quién fue el jefe del Computer Science Lab en su día.

Lugar de Creación: Suecia

Tipo de Paradigma: Funcional

Fue influenciado por: Prolog

Ha Influenciado A: Clojure, Scala

Instalación[editar]

Diríjase a la siguiente dirección http://erlang.org/download.html y descargue la última versión de Erlang.

Instalación en Windows[editar]

  1. El sistema es descargado como un único archivo .exe.
  2. Damos doble clic en el icono del archivo .exe y seguimos las instrucciones.

Instalación en Unix[editar]

Una vez instalado, el sistema completo, a excepción de un pequeño script start-up de inicio, reside en un solo directorio.

La ubicación de este directorio puede ser elegido arbitrariamente por el instalador, y no necesitan estar en el $ PATH del usuario. Los únicos requisitos son que el sistema de archivos donde se encuentra tenga suficiente espacio libre, y que los usuarios que ejecutan Erlang / OTP tengan acceso de lectura a la misma. En el siguiente ejemplo, el directorio se supone que se encuentra en / usr / local / Erlang, que se llama aquí el directorio superior.

Se supone que usted tiene el archivo comprimido tar, el nombre de las cuales es <prefix>. tar.gz, donde <prefix> es una cadena que denota la versión particular Erlang/OTP, por ejemplo, otp LXA 11930 sunos5 R9B.

Cualquiera que sea la cadena <prefix> utilizada a continuación, debería ser sustituido por el prefijo del nombre real del archivo comprimido tar.

El archivo no tiene un solo directorio en el que se guardan todos los demás archivos. Por tanto, el archivo debe ser extraído en un directorio vacío de directorio.

  • Si el directorio superior no existe, creelo:
mkdir /usr/local/erlang
  • Cambie el directorio actual al directorio superior:
cd /usr/local/erlang
  • Cree el directorio de instalación con un nombre apropiado.
Por ejemplo:
mkdir otp r7b
  • Cambie al directorio de instalación.
Por ejemplo:
cd otp r7b
  • Suponiendo que el archivo comprimido tar reside en el directorio <SOME-DIR>, extraer el archivo comprimido tar en el directorio actual:
gunzip -c <SOME-DIR>/<PREFIX>.tar.gz | tar xfp –
  • Lea el archivo README en el directorio de instalación por actualizaciones de última hora, antes de proceder.
  • Ejecute el script de instalación en el directorio de instalación, con la ruta absoluta de del directorio de instalación como argumento, ./Install /usr/local/erlang/otp r7b. En la mayoría de los casos, hay una respuesta por defecto entre corchetes ([]). Si es satisfactoria la que esta por defecto, solo presione enter <Return>.
En general sólo se le pide una cosa:
¿Quieres usar un inicio de sistema mínimo en lugar del inio SASL?
En un sistema mínimo, sólo el kernel y las aplicaciones STDLIB se cargan y se inicia. Si el Inicio SASL es utilizado, la aplicación SASL está incluido también. Normalmente, el sistema mínimo.
  • Hacer Erlang/OTP disponible para los usuarios, ya sea poniendo la ruta de acceso /usr/local/erlang/otp r7b/bin en la variable $ PATH de usuarios o enlazar el ejecutable /usr/local/erlang/otp r7b/bin/erl en consecuencia.
Por ejemplo:
ln -s /usr/local/erlang/otp r7b/bin/erl /usr/local/bin/erl

Verificación de la instalación en Windows y Unix[editar]

Verificación de la instalación en Windows[editar]

Ahora hay que verificar que la instalación fue exitosa.

  • Inicia Erlang dando doble-clic en el acceso directo de Erlang en el escritorio. Espera a que se muestre una ventana de línea de comandos con la siguiente salida.
  • Iniciar la barra de herramientas de la consola de Erlang
1> toolbar:start().
Verifique que la barra de herramientas se muestre.
Nota: El punto final (".") es un marcador final de todos los comandos en la consola de Erlang y debe ser introducida para un comando para iniciar la ejecución.
  • Introduzca el comando halt(),
2> halt().

Lo cual debería terminar las dos, la barra de herramientas y la ventana de línea de comandos.

Verificacion Unix[editar]

  • Inicie Erlang desde línea de comando
unix> erl
  • Espere la siguiente salida
Erlang R13B01 (erts-5.7.2) [smp:2:2] [rq:2] [async-threads:0]
Eshell V5.7.2  (abort with ^G)
1>
  • Iniciar la barra de herramientas de la consola de Erlang
1> toolbar:start().
Verifique que la barra de herramientas se muestre.
"Nota: El punto final (".") es un marcador final de todos los comandos en la consola de Erlang, y debe ser introducida para un comando para iniciar la ejecución."
  • Introduzca el comando halt()
2>halt().
Lo cual debería terminar las dos, la barra de herramientas y la ventana de línea de comandos.

Uso básico de Erlang[editar]

Iniciando Erlang[editar]

Si está ejecutando en un sistema Unix teclee "erl" o si se está ejecutando en Windows inicie Erlang haciendo clic en el icono de inicio de Erlang. Debería ver algo como esto:

Erlang R13B01 (erts-5.7.2) [smp:2:2] [rq:2] [async-threads:0]
Eshell V5.7.2  (abort with ^G)
1>

El signo ">" significa que el sistema está esperando por una entrada.


Usando Erlang como calculadora

1> 2131836812671*12937192739173917823.
27579983733990928813319999135233
2>

Recuerde que debe terminar cada expresión con un punto seguido de un espacio en blanco.

Editar expresiones anteriores[editar]

Las expresiones anteriores se pueden recuperar y editar usando simples comandos de edición emacs. Los más comunes son:

^ P

Recuperar la línea anterior.

^N

Buscar la línea siguiente.

^ A

Ir al principio de la línea actual.

^ E

Ir al final de la línea actual.

^ D

Eliminar el carácter bajo el cursor.

^ F

Ir hacia delante por un solo carácter.

^ B

Regresar por un carácter.

Enter

Evaluar el comando actual.

Nota: ^ X significa presionar Ctrl + X

Compilando su primer programa[editar]

Escriba lo siguiente en un archivo utilizando su editor de texto favorito:

-module(test).
-export([fac/1]).

fac(0) -> 1;
fac(N) when N > 0 -> N * fac(N-1).

Guarde este archivo llamado test.erl, el nombre del archivo debe ser el mismo que el nombre del módulo. Compile el programa escribiendo c(test) y luego ejecutelo:

3> c(test).
{ok,test}
4> test:fac(20).
2432902008176640000
5> test:fac(40). 
815915283247897734345611269596115894272000000000
6> 

Nota: el archivo debe ser guardado en el mismo directorio en donde inicio erl

“Hola Mundo”[editar]

Programa Hola Mundo[editar]

Erlang es un poco diferente a otros lenguajes funcionales como Haskell. En Erlang primero se tiene que crear un modulo con extensión erl. Cada instrucción termina con un punto, sino se hace esto el compilador no se da cuenta cuándo acaba la instrucción. En el archivo creado hay que escribir: -module(archivo.erl). Donde archivo es el nombre del documento que se creó. Después se exportan las funciones que se pueden usar por la consola mediante la instrucción: -export([función/1]). Para compilar el código se escribe en la consola c(archivo). Esto compila el programa. El número que acompaña el nombre de la instrucción dice cuántos parámetros acepta.

Cree un archivo en “C:\Program Files\erl5.7.2\usr” o escriba “pwd().” Para saber a dónde debe crear los módulos. A continuación haremos nuestro primer programa.

Primer.erl[editar]

   -module(primer).
   -export([hello_world/0]).
  
   hello_world() -> 
          "hello world".


Para compilar escriba en el Shell c(primer). Acuérdese de los puntos al final de la instrucción. Para ejecutar las funciones de los modulos en el Shell usted debe escribir: “modulo:función(parametros).” Por tanto este programa se ejecuto: “primer:hello_world().”.

   1>c(primer).
   {ok,primer}
   2>primer:hello_world().
   "hello world"
   3>

Diccionario de palabras reservadas[editar]

Palabras Reservadas[editar]

Las palabras reservadas en Erlang son las siguientes:

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

Operadores y Simbolos del lenguaje[editar]

Operadores de Control[editar]

Simbolo Propósito
, Separador de Comando
; Separador de Comando
. Finalizador de Comando
-> Declaración de Funciones o Guard
% Comentario

Erlang puede ser un poco confuso ya que existen 2 tipos de separadores de comando. La , se utiliza para separar expresiones. Esto generalmente se utiliza cuando se tiene una expresión después de otra como es en la programación secuencial. El separador ; es utilizado cuando existen distintos niveles de comandos que necesitan ser finalizados como en el caso de case o guard. El . se utiliza para terminar una función (o expresión, si se está corriendo desde la consola). Los comentarios inician con un % y cubre el resto de la oración pero lo el final, el final de un comentario es marcado por un “ “ (espacio).

Expresiones Aritmeticas[editar]

Simbolo Proposito Tipo de Datos
+ Suma Enteros o Flotantes
- Resta Enteroso o Flotantes
* Multiplicación Enteros o Flotantes
div División de Enteros Enteros
/ División de Punto Flotante Enteros
rem División Modular Entero

Erlang utiliza notación infija para las operaciones aritméticas. Por ejemplo: (2+3)*2 se evaluaría a 10 como esperado. Ya que Erlang es el tipo de lenguaje dinámico se requiere un operador especial para prevenir la coerción a un punto flotante. Utilizando el operador / causaría la misma conversión implícita.

Ejemplos:

  1> +1.
  1
  2> -1.
  -1
  3> 1+1.
  2
  4> 4/2.
  2.0
  5> 5 div 2.
  2
  6> 5 rem 2.
  1

Operadores Áritmeticos de Bit a Bit[editar]

Símbolo Propósito Tipo de Datos
band Y Lógica Entero
bor O Lógica Entero
bxor O exclusiva Lógica Entero
bnot No Lógico Unario Entero
bsl Bitshift Left Entero
bsr Bitshift Right Entero

Ejemplos:

  0
  2> 2#10 bor 2#01.
  3
  3> a + 10.
  ** exception error: bad argument in an arithmetic expression
       in operator  +/2
          called as a + 10
  4> 1 bsl (1 bsl 64).
  ** exception error: a system limit has been reached
       in operator  bsl/2
          called as 1 bsl 18446744073709551616

Operadores Lógicos[editar]

Símbolo Propósito
and Y Lógica (AND)
or O Lógica (OR)
xor O Exclusiva (XOR)
not No Lógica (NOT)

Ejemplos:

  1> not true.
  false
  2> true and false.
  false
  3> true xor false.
  true
  4> true or garbage.
  ** exception error: bad argument
       in operator  or/2
          called as true or garbage

Tipos de datos fundamentales disponibles en el lenguaje[editar]

Condiciones[editar]

Un pedazo de datos de cualquier tipo de datos se llama un término.

Números[editar]

Hay dos tipos de literales numéricos, enteros y carrozas. Además de la notación convencional, hay dos Erlang-notaciones específicas:

  • $ Char

El valor ASCII de los caracteres de caracteres.

    1. base de valor

Entero con la base de base, que debe ser un entero en el rango de 2 .. 36.

Atoms[editar]

Un átomo es un literal, una constante con nombre. Un átomo debe estar entre comillas simples ( ') si no empiezan con una letra minúscula o si contiene caracteres que no sean caracteres alfanuméricos, guiones bajos (_), o @.

Bit Cuerdas y Binarios[editar]

Una cadena de bits que se utiliza para almacenar un área de memoria sin tipo. Cadenas se expresan mediante la sintaxis de bits. Las cadenas de bits que consta de un número de bits que es divisible por ocho son llamados Binarios

Referencia[editar]

Una referencia es un término que es único en un sistema de tiempo de ejecución de Erlang, creado por llamar make_ref / 0.

Fun[editar]

Funs hacer posible la creación de una función anónima y pasa a la función por si misma - no su nombre - como argumento a otras funciones.

Ejemplos:

 1> Fun1 = fun (X) -> X+1 end.
 #Fun<erl_eval.6.39074546>
 2> Fun1(2).
 3

Tupla[editar]

Compuesto de tipo de datos con un número fijo de términos:

{Term1,...,TermN} Término1 ,..., TermN () 

Cada término de duración en la tupla se llama un elemento. El número de elementos se dice que es el tamaño de la tupla. Existe un número de BIFS para manipular tuplas.

Lista[editar]

Compuesto de tipo de datos con un número variable de los términos.

[Term1,...,TermN] [Término1 ,..., TermN] 

Cada término de término de la lista se llama un elemento. El número de elementos se dice que es la longitud de la lista. Formalmente, la lista es o bien la lista vacía [] o consista en una cabeza (primer elemento) y una cola (el resto de la lista) que también es una lista. Este último puede ser expresado como [H | T]. La notación [término1 ,..., TermN] anterior es en realidad la abreviatura de la lista [término1 |[...|[ TermN |[]]]].

Ejemplo:

[] Es una lista, por lo tanto
[c | []] es una lista, por lo tanto
[b | [c |[]]] es una lista, por lo tanto
[a,b,c] . [a | [b | [c |[]]]] es una lista, o en definitiva [a, b, c].

Una lista en la cola es una lista se llama a veces una lista adecuada. Se permite tener una lista en la que la cola no es una lista, por ejemplo, [a | b]. Sin embargo, este tipo de lista es de poca utilidad práctica.

Ejemplos:

 1> L1 = [a, 2, (c, 4)]. [A, 2, (c, 4)] 2> [H | T] = L1. [A, 2, (c, 4)] 3> H. de 4> T. [2, (c, 4)] 5> L2 = [d | T]. [d, 2, (c, 4)] 
 6> longitud (L1). 3 7> longitud ([ ]). 0 

Una colección de funciones de procesamiento de la lista se puede encontrar en las listas stdlib módulo.

String[editar]

Las cadenas se escriben entre comillas ( "), pero no es un tipo de datos en Erlang. En lugar de una cadena" Hola "es la abreviatura de la lista [e $ $ h,, $ l, l $, $ o], que es [ 104.101.108.108.111]. Dos cadenas literales adyacentes se concatenan en una sola. Esto se hace en tiempo de compilación y no incurrir en gastos de tiempo de ejecución.

Ejemplo:

"string" "42" "cadena" "42" es equivalente a "string42" "string42"

Registro[editar]

Un registro es una estructura de datos para almacenar un número fijo de elementos. Sin embargo, registro no es un tipo de datos real. Las expresiones de registro son traducidos a tupla expresiones durante la compilación. Por lo tanto, las expresiones de registro no son comprendidos por el depósito si no se adoptan medidas especiales.

  -module (persona).
  -export ([new/2]).
  
  registro (persona, (nombre, edad)).
  
  nuevo (nombre, edad) -> 
        # (name = Nombre de la persona, edad = edad).
  1> persona:nuevo(Ernie,44).
  (persona,Ernie,44)

Boolean[editar]

No hay ningún tipo de datos Boolean en Erlang. En cambio los átomos de verdadero y falso se usa para designar a los valores booleanos.

Operaciones de entrada y salida básicas[editar]

Al ser Erlang un lenguaje funcional, comparte muchas cosas con Haskell.

Operaciones de entrada[editar]

Como muchos lenguajes funcionales Erlang posee su propio Shell en el cual se puede escribir directamente código y evaluarlos (correrlos) para así ver que pasa.

Al empezar el interprete se correrá el Shell, y se vera algo así:

  % erl
  Erlang (BEAM) emulator version 5.2 [source] [hipe]
        
  Eshell V5.2  (abort with ^G)
  1>

Si escribimos 2 + 5 en Erlang obtenemos lo siguiente:

  1> 2 + 5.
  7
  2>

Operaciones de salida[editar]

Al igual que en la mayoría de los lenguajes funcionales, no se envía mensajes de salida, sino que estos envían respuestas a las entradas.

 2> (42 + 77) * 66 / 3.
 2618.0

También se pueden hacer métodos, y luego llamarlos para que estos nos den una salida

 -module(tut).
 -export([double/1]).
 
 double(X) ->
     2 * X.
 4> tut:double(10).
 20

Asi que como vemos, al igual que en Haskell.

Tipos de datos complejos y su representación[editar]

Registros[editar]

Un registro es una estructura de datos con un número fijo de campos que se accede por su nombre, similar a una estructura de C o un registro de Pascal. Esto difiere de tuplas, donde los campos son visitados por la posición. En el caso del ejemplo de persona, defina un tipo de registro como sigue: -record(person, {name,age,phone}). Esto introduce el tipo de registro person, donde cada instancia de registro contiene tres campos llamados name, age y phone. Los nombres de campo se definen como los átomos. Aquí hay un ejemplo de una instancia de registro de este tipo:

#person{name="Joe", age=21, phone="999-999"}

En el código anterior, #person es el constructor de registros person. Lo que pasa en este ejemplo es que hicimos una lista de los campos en el mismo orden que en la definición, pero esto no es necesario. La siguiente expresión da el mismo valor:

-record(person, {name,age=0,phone=""}).

Ahora, un registro persona como esta:

#person{name="Fred"}

tendrá cero años de edad y un número de teléfono vacío; en ausencia de un valor por defecto especificado, "predeterminado predeterminado" es el átomo no definido.

La definición general de un registro name con campos denominados campo1 a campon tomará la siguiente forma:

-record{name, {field1 [ = default1 ],

                         field2 [ = default2 ],
                         ...
                         fieldn [ = defaultn ] }

donde las partes entre corchetes son las declaraciones opcionales de valores de campo por defecto. El mismo nombre de campo se puede utilizar en más de un tipo de registro; de hecho, dos registros podrían compartir la misma lista de nombres. El nombre del registro puede ser utilizado en sólo una definición, sin embargo, como este se utiliza para identificar el registro.

Trabajando con Registros[editar]

Suponga que le dan un valor de registro. ¿Cómo se puede acceder a los campos, y cómo puede usted describir un registro modificado?. Teniendo en cuenta el siguiente ejemplo:

Person = #person{name="Fred"}

se accesa a los campos de los registros de esta manera: Person#person.name, Person#person.age, y así sucesivamente. ¿Cuáles serán los valores de estos? La forma general para acceder a este campo seria:

RecordExp#name.fieldName

donde name y fieldName no pueden ser variables y RecordExp es una expresión que denota un registro. Normalmente, esta sería una variable, pero también podría ser el resultado de una aplicación de la función o de un acceso al campo para otro tipo de registro.

Supongamos que desea modificar un solo campo de un registro. Usted puede escribir directamente, como en el texto siguiente:

NewPerson = Person#person{age=37}

En tal caso, la sintaxis del registro es una ventaja real. Usted ha mencionado sólo el campo cuyo valor es modificado; los que no se han modificado de Person a NewPerson no es necesario la figurarlo en la definición. De hecho, el mecanismo de registro permite a cualquier selección de los campos que se actualizará, como en:

NewPerson = Person#person{phone="999-999",age=37}

El caso general será:

RecordExp#name{..., fieldNamei=valuei, ... }

donde los campo actualizados pueden ocurrir en cualquier orden, pero cada nombre de campo puede ocurrir, como máximo, sólo una vez.

Implementación de registros[editar]

El compilador de Erlang implementa registros antes de que se ejecute programas. Los registros se traducen tuplas, y funciones en los registros se traducen en funciones y BIFs sobre el tuplas correspondientes.

    • Tuplas: son un tipo de datos compuestos utilizados para almacenar una colección de items, que son valores de datos de Erlang pero que no tienen que ser todos del mismo tipo. Tuplas están delimitados por llaves, {...}, y sus elementos están separados por comas. Algunos ejemplos de tuplas incluyen:
   {123, bcd} {123, def, abc} {abc, {def, 123}, ghi} {}
 {person, 'Joe', 'Armstrong'} {person, 'Mike', 'Williams'}

Listas[editar]

Listas y tuplas se utilizan para almacenar colecciones de elementos; en ambos casos, los elementos pueden ser de diferentes tipos, y las colecciones pueden ser de cualquier tamaño. Las listas y las tuplas son muy diferentes, sin embargo, en la forma en que pueden ser procesados. Comenzaremos por describir cómo las listas se denotan en Erlang, y examinar la forma en que las cadenas son un tipo especial de lista, antes de explicar en detalle cómo las listas pueden ser procesadas.

Listas están delimitadas por corchetes, [...], y sus elementos están separados por comas. Elementos en listas no tienen que ser del mismo tipo de datos y, como las tuplas, se puede ser libremente mezclados. Algunos ejemplos de listas son:

[january, february, march]
[123, def, abc]
[a,[b,[c,d,e],f], g]
[]
[{person, 'Joe', 'Armstrong'}, {person, 'Robert', 'Virding'},
{person, 'Mike', 'Williams'}]
[72,101,108,108,111,32,87,111,114,108,100]
[$H,$e,$l,$l,$o,$ ,$W,$o,$r,$l,$d]
"Hello World"

La lista [a, [b, [c, d, e], f], g] se dice que tiene una longitud de 3. El primer elemento es el átomo a, la segunda es la lista [b, [c, d, e], f], y el tercero es el átomo g. La lista vacía es denotada por [], mientras que [{person, 'Joe', 'Armstrong'}, {person, 'Robert', 'Virding'}, {person, 'Mike', Williams'}] es una lista de tuplas etiquetadas.

Arrays[editar]

El módulo array contiene un tipo de dato abstracto funcional, matrices extendibles. Pueden tener un tamaño fijo, o crecer según sea necesario. El módulo contiene la funcionalidad para establecer y revisar los valores, así como para definir recurrencias sobre ellos.

los arreglos utilizan la indexación basada en cero. Esta es una elección deliberada de diseño y difiere de Erlang otras estructuras de datos, por ejemplo, tuplas.

Salvo que se indique por el usuario cuando se crea la matriz, el valor por defecto es el átomo no definido. No hay ninguna diferencia entre una entrada no establecida y una entrada que se ha establecido explícitamente en el mismo valor como predeterminado. Si tiene necesidad de diferenciar entre entrados establecidas y no establecidas, debe asegurarse de que el valor predeterminado no se puede confundir con los valores de las entradas establecidas.

El arreglo no se reduce de forma automática; si un índice que se ha utilizado con éxito para establecer una entrada, todos los índices en el intervalo [0, I] se quedara accesible a menos que el tamaño de la matriz está explícitamente cambiado.

Ejemplos:

%% crear un arreglo con tamaño predeterminado con entradas 0-9 indicadas como indefinido

 A0 = array:new(10).
 10 = array:size(A0).

                %% Crear un arreglo extendible e indicando la entrada 17 como verdadera,
 %% causando que el arreglo crezca automaticamente
 A1 = array:set(17, true, array:new()).
 18 = array:size(A1).

 %% Leer un valor guardado
 true = array:get(17, A1).

 %% Accesando un valor no indicado retorna el valor por defecto
 undefined = array:get(3, A1).

                %% Accesando un valor mas alla de la ultima entrada indicada también retorna el
 %% valor por defecto, si el arreglo no tiene tamaño definido
 undefined = array:get(18, A1).

                %% funciones "sparse" (esparcidas) ignoran las entradas con valores por defecto
 A2 = array:set(4, false, A1).
 [{4, false}, {17, true}] = array:sparse_to_orddict(A2).

 %% Un arreglo extendible puede después ser de tamaño fijo
 A3 = array:fix(A2).

 %% Un arreglo de tamaño fijo no cece automaticamente y no
 %% permite ser accesado mas alla de la ultima entrada indicada
 {'EXIT',{badarg,_}} = (catch array:set(18, true, A3)).
 {'EXIT',{badarg,_}} = (catch array:get(18, A3)).

Estructura de Datos Complejas[editar]

Cuando nos referimos a términos de Erlang, nos referimos a estructuras de datos legales. Los Términos Erlang pueden ser valores de datos simples, pero a menudo utilizamos la expresión para describir arbitrariamente estructuras de datos complejas. En Erlang, las estructuras de datos complejas son creados anidadando tipos de datos compuestos. Estas estructuras de datos puede contener variables ligadas o los valores simples y compuestos ellos mismos. Un ejemplo de una lista que conteniendo tuplas tipo person (etiquetados con el átomo person) con el nombre, apellido, y una lista de atributos que se vería así:

 [{person,"Joe","Armstrong",
 [ {shoeSize,42},
 {pets,[{cat,zorro},{cat,daisy}]},
 {children,[{thomas,21},{claire,17}]}]
 },
 {person,"Mike","Williams",
 [ {shoeSize,41},
 {likes,[boats,wine]}]
 }
 ]

O bien, si tuviéramos que escribir en unos pocos pasos utilizando variables, lo haríamos así. Note cómo, para facilitar la lectura, hemos llamado a las variables con sus tipos de datos:

 1> JoeAttributeList = [{shoeSize,42}, {pets,[{cat, zorro},{cat,daisy}]},
 1> {children,[{thomas,21},{claire,17}]}].
 [{shoeSize,42},
 {pets,[{cat,zorro},{cat,daisy}]},
 {children,[{thomas,21},{claire,17}]}]
 2> JoeTuple = {person,"Joe","Armstrong",JoeAttributeList}.
 {person,"Joe","Armstrong",
 [{shoeSize,42},
 {pets,[{cat,zorro},{cat,daisy}]},
 {children,[{thomas,21},{claire,17}]}]}
 3> MikeAttributeList = [{shoeSize,41},{likes,[boats,wine]}].
 [{shoeSize,41},{likes,[boats,wine]}]
 4> MikeTuple = {person,"Mike","Williams",MikeAttributeList}.
 {person,"Mike","Williams",
 [{shoeSize,41},{likes,[boats,wine]}]}
 5> People = [JoeTuple,MikeTuple].
 [{person,"Joe","Armstrong",
 [{shoeSize,42},
 {pets,[{cat,zorro},{cat,daisy}]}, {children,[{thomas,21},{claire,17}]}]},
 {person,"Mike","Williams",
 [{shoeSize,41},{likes,[boats,wine]}]}]

Una de las bellezas de Erlang es el hecho de que no hay necesidad explícita de la asignación y cancelación de memoria. Para los programadores de C, esto significa que no hay más noches sin dormir la caza de errores de puntero o de fugas de memoria. La memoria para almacenar los tipos de datos complejos es asignado por el sistema de ejecución cuando sea necesario, y la cancelacion de forma automática por el recolector de basura cuando la estructura ya no es referenciada.


Declaración e Inicialización de Variables[editar]

Tipos de Variables[editar]

Erlang maneja diferentes tipos de variables, los cuales se dividen en 2:

  1. Constantes: estos tipos de dato son los que ya no son divisibles en más datos primitivos. Las Constantes pueden ser de 2 tipos:
    1. Números. Los números caben dentro de 2 tipos. Los tipos son:
      1. Enteros (Ejemplos: 120, -35000, 590, etc.)
      2. Flotantes (Ejemplos: 3.14159, -1.2e-45, 30.141216, etc.)
    2. Átomos. Los átomos son simplemente constantes con nombres. (Ejemplos: abcdef, ‘Un atomo con espacios’, lunes, martes, sábado, Hola_Mundo, etc.)
  2. Compuestos: estos son los tipos de dato que se usan para agrupar datos. Los tipos son:
    1. Tuplas. Las tuplas son usadas para almacenar un número fijo de datos y se utilizan mediante ‘{}’. Las tuplas son similares a los registros o estructuras para los lenguajes funcionales. (Ejemplo: {a,12,77},{},{1,2,3},etc.)
    2. Listas. Las listas son utilizadas para almacenar un número variable de datos y son escritos como secuencias de datos que son cerradas mediante ‘[]’. (Ejemplos: [], [1,2,3,34],[1,’Hola Mundo’],etc.)

Declaración de Variables[editar]

La declaración de variables es diferente a Erlang que a otros leguajes, ya que Erlang no define una variable de tipo int o float. Sino que este se da cuenta al volverse le a asignar. Las variables de Erlang son de simple asignamiento. Comienzan con una letra capital o un ‘underscore’ seguidas de un texto alfanumérico.

Tabla de Operadores con Asociatividad y Precedencia[editar]

En expresiones que consisten de sub-expresiones los operadores serán desarrollados de acuerdo a su precedencia definida.

Precedencia (de más alta a más baja)
Símbolo Asociatividad
:
#
Unary + - bnot not
/ * div rem band and Izquierda
+ - bor bxor bsl bsr or xor Izquierda
++ -- Derecha
== /= =< < >= > =:= =/=
andalso
orelse
= ! Derecha
catch

Los operadores con la prioridad más alta serán los primeros en evaluarse. Operadores con la misma prioridad serán evaluados por su asociatividad. Los operadores aritméticos con asociatividad por la izquierda son evaluados de izquierda a derecha.

Ejemplo:
6 + 5 * 4 - 3 / 2 => 6 + 20 - 1.5 => 26 - 1.5 => 24.5

Expresiones booleanas de Corto Circuito[editar]

Ejemplo:

 Expr1 orelse Expr2
 Expr1 andalso Expr2
   

Estas son expresiones booleanas en las que Expr2 será evaluada solo si es necesario. En una expresión orelse, Expr2 se evaluara si y solo si Expr1 se evalúa a false. En una expresión andalso, Expr2 será evaluado si y solo si Expr1 se evalúa a true.

Ejemplo:

if A >= 0 andalso math:sqrt(A) > B -> ...

if is_list(L) andalso length(L) == 1 -> ...

Estructuras de decisión e iteración[editar]

IF[editar]

  if
     GuardSeq1 ->
          Body1;
     ...;
     GuardSeqN ->
          BodyN
  end

Las ramas de un si-expresión se analizan de forma secuencial hasta que una secuencia de guardia GuardSeq que se evalúa como verdadera se encuentra. Entonces, el organismo correspondiente (la secuencia de expresiones separadas por ',') es evaluada. El valor de retorno del cuerpo es el valor de retorno de la expresión del IF.

Si no hay secuencia de guardia es cierto, una carrera if_clause tiempo de error. Si es necesario, la expresión de la guardia real puede ser utilizado en la última rama, ya que la secuencia de la guardia siempre es cierto.

  is_greater_than(X,Y) ->
      if
        X>Y ->
          true;
        true -> % works on an ´else´ branch
          false
      end

Case[editar]

La expresión expr es evaluado y el patrón de los patrones se comparan con la secuencia de resultados. Si un partido tiene éxito y la secuencia de la guardia opcional GuardSeq es cierto, el organismo correspondiente se evalúa.
El valor de retorno del cuerpo es el valor de retorno de la expresión caso. Si no hay un patrón de coincidencia con una secuencia de la guardia real, una ejecución case_clause tiempo de error.

   case Expr of
       Pattern1 [when GuardSeq1] ->
          Body;
       ....;
       PatternN [when GuardSeqN] ->
          BodyN
   end

ITERACIONES Y RECURSIONES[editar]

Guard sequences[editar]

Una secuencia de guardia es un conjunto de guardias separados por punto y coma (;). La secuencia de guardia es cierto si al menos uno de los guardias es cierto. GUARD1, ...; GuardK Un guard es un conjunto de expresiones del guard , separadas por comas (,). El guard es cierto si todas las expresiones de la Guardia resultado verdadero. GuardExpr1, ..., GuardExprN Las expresiones del Guard permitido (a veces se llaman pruebas de guard) es un subconjunto del conjunto de las expresiones válidas Erlang, desde la evaluación de una expresión de guard debe ser garantizado de estar libre de efectos secundarios.

Expresiones Validas de los Guards
atom true
Otras constantes (términos y variables ligadas), todos considerados como falsos
Comparacion de terminos
Expresiones aritmeticas y booleanas
Llamadas al BIFs (Built-in functions) especificadas abajo
Tipo de prueba BIFs Otros BIFS permitidos en los guards
is_atom/1 Float)
is_constant/1 float(Term)
is_integer/1 Float)
is_float/1 Float)
is_number/1 Binary)
is_reference/1 element(N, Tuple)
is_port/1 hd(List)
is_pid/1 tl(List)
is_function/1 length(List)
is_tuple/1 self()
is_record/2 The 2nd argument is the record name node()
is_list/1 Ref|Port)
is_binary/1

pequeño ejemplo:

   fact(N) when N>0 ->  % first clause head
       N * fact(N-1);   % first clause body
   fact(0) ->           % second clause head
           1.                    % second clause body

Tail Recursion[editar]

Si la última expresión de una función es una llamada de función, una llamada recursiva de tail se realiza de tal manera que no hay recursos del sistema (como la pila de llamadas) se consumen. Esto significa que un bucle infinito, como un servidor se puede programar de manera que sólo utiliza las llamadas tail recursivas.

fact(N) when N>1 -> fact(N, N-1); fact(N) when N=1; N=0 -> 1.

fact(F,0) -> F; % The variable F is used as an accumulator fact(F,N) -> fact(F*N, N-1).


Ejemplo 1:

Construye un lista, imprime la function, aplicando la function a la lista lists:map(). Retorna un valor.

counter1() -> L = lists:seq(1,10), PrintInt = fun(I) -> io:fwrite("~p ", [I]) end, lists:foreach(PrintInt, L). counter3() -> L = lists:seq(1,10), [ printInt(I) || I <- L].

Ejemplo 2:

Recursión con un acumulador. usar un guard sequence ("cuando N > 10") para especificar el caso base. Se define dos funciones "counter4/0" (zero args) y "counter4/1".

"counter4/1" consiste en dos clausulas separadas por ; counter4() -> counter4(1). counter4(N) when N > 10 -> none; counter4(N) -> printInt(N), counter4(N + 1).

Ejemplo 3: Procesamiento de la lista con un acumulador

counter6() -> %L = [1,1,1,1,1,1,1,1,1,1], L = lists:duplicate(10,1), counter6(L, 0). counter6([], Acc) -> Acc; counter6([H|T], Acc) -> N = H + Acc, printInt(N), counter6(T, N).

Ejemplo 4: Procesamieinto de una matriz con recursividad count_matrix1() -> count_matrix1( 5, 0, 0 ). count_matrix1(Size, Size, Size) -> none; count_matrix1(Size, Size, PtrY) -> io:format("~n"), count_matrix1(Size, 0, PtrY+1); count_matrix1(Size, PtrX, PtrY) -> io:format("(~p,~p) ", [PtrX, PtrY]), count_matrix1(Size, PtrX+1, PtrY).  

Declaración, definición y uso de métodos y funciones[editar]

Métodos y funciones en Erlang[editar]

Para ver una función en Erlang podemos el archivo even_prime.er con el siguiente código -module(even_prime). % 1 -export([is_even_prime/1]). % 2

                                              % 3 

is_even_prime(2) -> % 4 clause 1 is simple

   true;                                      % 5

is_even_prime(N) when is_integer(N) -> % 6 clause 2 has a guard: is_integer(N)

   false;                                     % 7

is_even_prime(Any) -> % 8 clause 3 is simple

   'I prefer integer inputs'.                 % 9

Las clausulas de las funciones están puestas en el orden en el cual se verifican. Primero is_even_prime(2) es verificada para ver si concuerda. Si el argumento es verificado dicha función regresa verdadero. Si is_even_prime(2) no es verificada, entonces se trata is_even_prime(N). is_even_prime(N) es verificada. La declaración when is_integer es una guardia que solo admite tipos integer a N. al final nos dice que la función se terminad definiendo. is_even_prime(Any) coincide con cualquier cosa de cualquier tipo y regresa “i prefer integer imputs” . la función se ha terminado. Esta función es una función total y cumple todos los los posibles argumentos de entrada. Salidas 2> c(even_prime). ./even_prime.erl:8: Warning: variable 'Any' is unused {ok,even_prime}

3> even_prime:is_even_prime(2). true

4> even_prime:is_even_prime(1). false

5> even_prime:is_even_prime(seven). 'I prefer integer inputs'

Estructura de una función[editar]

================================================================== Syntax/structure of a function: ================================================================== semicolon - ends a clause period - ends a function when - starts a guard arrow - separates the head from the tail of the function function head - input part of function includes the signature and guard function tail - output/consequence(s) part of function signature - the function name and argument structure/count ================================================================== rotate_list( [H|T] ) when is_atom(H) -> T ++ [H].

                                    .

[----signature-----] [----guard----] .

                                    .                     

[-----------function head----------] . [--function tail--]

==================================================================

Implementación y uso de la Programación Orientada a Objetos[editar]

Objetos[editar]

Erlang no tiene explícitamente incorporado características de lenguaje orientado a objetos. Un estilo de programación orientado a objetos se puede lograr por otros medios fácilmente. Es especialmente fácil de hacer programación orientada a objetos, si nos limitamos a la herencia simple. Se puede utilizar procesos para representar a las clases y mensajes para representar a los métodos. Para ello, cada objeto cuando se creó puede crear una cadena de procesos que representan a sus antepasados en la cadena de herencia. Los métodos (mensajes) se puede pasar por la cadena hasta llegar a un proceso que tiene un método de emparejamiento. Si el mensaje llega a la cima de la cadena (la clase objeto o superior que dev_nul), entonces podemos generar una excepción para el “nombre de un método malo". Cada clase (proceso) mantendrá sus propios atributos (variables) en su propia lista recursiva de la llamada de argumentos. Estos atributos se pueden acceder y actualizar con mensajes así como get y set. Los atributos son almacenados en un diccionario llamado Bundle, en cada proceso de clase.

Se incluye un código de ejemplo que crea Programación Orientada a Objetos utilizando la técnica descrita. En el programa, creamos una instancia de la clase entero. Su padre real, sabe cómo tomar la raíz cuadrada de números reales. su padre complejo, sabe cómo tomar la raíz cuadrada de números negativos. Su padre matriz, sabe cómo tomar la raíz cuadrada de una matriz diagonal.

Lógicamente, la relación de clase tradicional es sotenida en el diagrama de clase. Un entero es un Real. un Real (float) es un (subconjunto de) complejo. Un número complejo es un (subconjunto de) matrices complejas, si pensamos en una matriz (1 por 1) como un solo número.

Un mensaje (método) es enviado a una instancia de un objeto. Si un proceso no sabe cómo hacer algo, pasa un mensaje (método) a su padre (proceso en esta situación). Si tratamos de hacer algo como tomar la raíz cuadrada de una matriz diagonal será pasada hasta dev_nul y generara un error.

La función inicial crea una instancia de la clase entero. Después le pregunta a la instancia que calcule la raíz cuadrada de 4 números: 4, 0.04, -4 y la matriz 4,0,0,9. Las respuestas son: 2, 0.2, {2,i} y 2,0,0,3.

Esquema de Administración de Memoria y Separación de Memoria[editar]

maneja todos sus procedimientos a través del heap ya que este no utiliza stack. Hay varias estrategias utilizadas para el manejamiento y mantenimiento del Heap. Entre ellas están:

  • “process local heap”
  • “unified heap”

"Process Local Heap"[editar]

Esta estrategia lo que realiza es crear una heap local donde ubica cada objeto. Cada proceso llama una colección independiente. El recolector de basura trabaja por procesos.

"Unified Heap"[editar]

Esta estrategia toma que todos los heaps son uno sólo que comparte todos los procesos. Este heap almacena la continuación de los procesos y los otros objetos alojados por proceso. Sólo caundo la heap es acabada por uno de estos procesos que el recolector de basura es llamado. El algoritmo utilizado en el Erlang basado para los sistemas ETOS es una combinación entre two-spacecopying y mark-and-sweep.

Algoritmo del Recolector de Basura para “Process Local Heap”[editar]

El algoritmo se divide en 2 pasos. El primero de ellos, realiza una barrida de todo el heap y lo compacta. El último, mueve los objetos de regreso hasta su destinación final. La gran ventaja de este algoritmo es que no ocupa espacio adicional. Sin embargo, en el otro aspecto el tiempo de corrida de este algoritmo depende del tamaño del heap.

Manejamiento de Memoria para el “Unified Heap”[editar]

Para acomodarse al ambiente multitasking el unified heap puede crecer y achicarse así sea necesario en la evolución del programa que evidentemente se mantiene proporcional el tamaño del espacio de procesamiento del Sistema Operativo al de la data utilizada. Por el default, al final de un ciclo el tamaño del heap es redefinido para que quepa los datos ocupen la mitad del tamaño del heap.

Manejamiento de Procesos[editar]

La estrategia utilizada es un manejador de procesos por prioridad. Cada proceso tiene un número de punto flotante marcando su prioridad. Esto otorga un buen control, de cuando deben entrar y salir los procesos. Los descriptores de procesos son alojados en el “unified heap” como cualquier otro. Cada descriptor de procesos tiene un apuntador al siguiente proceso

Paso de Parametros[editar]

Erlang del “Process Local Heap” realiza una copia para pasar los parámetros de diferentes procesos. Ya que cada proceso tiene una colección diferente es por esta razón la necesidad de una copia. Sin embargo, gracias a que el “Unified Heap” toman la heap como un todo el paso de parámetros mediante procesos no genera un problema como en el “Process Local Heap”.

Implementación de Corutinas[editar]

Procesos[editar]

Erlang, a diferencia de otros lenguajes funcionales, tiene la capacidad de manejar concurrencia y programación distribuida. Concurrencia es poder manejar varios hilos de ejecución al mismo tiempo. Por ejemplo, sistemas operativos modernos le permitirían utilizar un procesador de textos, una hoja de cálculo, un cliente de correo y un trabajo de impresión todo esto ejecutándose al mismo tiempo. Por supuesto cada procesador (CPU) en el sistema probablemente sólo maneja un hilo (o trabajo) a la vez, pero cambia entre los trabajos a una velocidad que da la ilusión de correrlos todo al mismo tiempo. Es fácil crear hilos paralelos de ejecución en Erlang y es fácil permitir que estos hilos se comuniquen unos con otros. En Erlang a cada hilo de ejecución se le denomina proceso. Nota: El término "proceso" es utilizado, generalmente, cuando los hilos de ejecución no comparten datos uno con el otro y el termino "hilo" cuando ellos comparten datos de alguna manera. Los hilos de ejecución en Erlang no comparten datos, por eso se llaman procesos. El BIF (Built-in Function) spawn de Erlang es utilizado para crear un nuevo proceso spawn(Modulo, Funcion_Exportada, Lista de Argumentos) Ejemplo: (Parte I) -module(tut14).

-export([start/0, say_something/2]).

say_something(What, 0) ->

   done;

say_something(What, Times) ->

   io:format("~p~n", [What]),
   say_something(What, Times - 1).

start() ->

   spawn(tut14, say_something, [hello, 3]),
   spawn(tut14, say_something, [goodbye, 3]).


5> c(tut14). {ok,tut14} 6> tut14:say_something(hello, 3). hello hello hello done Podemos observar que la función say_something escribe su primer argumento la cantidad de veces especificada por el segundo argumento. Ahora si observamos a la función start. Este inicia dos procesos, uno que escribe “hello” tres veces y otro que escribe “goodbye” tres veces. Ambos de los procesos utilizan la función say_something. Nota: La función utilizada por spawn para iniciar un proceso fue exportado desde un modulo (Ej. En el -export al inicio del modulo). (Parte II) 9> tut14:start(). hello goodbye <0.63.0> hello goodbye hello goodbye

Podemos notar que en esta parte no se escribió “hello” tres veces y después “goodbye” tres veces, en vez el primer proceso escribió “hello” y el Segundo “goodbye”, el siguiente otro “hello”, etc… Ahora, de donde sale el <0.63.0>? Lo que retorna una función es el valor de la ultima “cosa” que realize la función. Lo último que hace la función start es

spawn(tut14, say_something, [goodbye, 3]). spawn retorna el identificador de un proceso (Process Identifier o pid), este identifica de manera unica un proceso.Asi que <0.63.0> es el pid que la función spawn llama. Nota: Observe que en io:format se utilize ~p en vez de ~w. Esto es así ya que, ~p escribe la data en una sintaxis estándar, de la misma manera que ~w, sin embargo este rompe los terms que al imprimir son más largos de una línea en varias líneas y las indenta de manera coherente. También intenta detectar listas con caracteres imprimibles e imprimirlos como strings.

Como enviar mensajes[editar]

En el siguiente ejemplo se crean dos procesos que se envían mensajes el uno al otro un determinado número de veces. Ejemplo: -module(tut15).

-export([start/0, ping/2, pong/0]).

ping(0, Pong_PID) ->

   Pong_PID ! finished,
   io:format("ping finished~n", []);

ping(N, Pong_PID) ->

   Pong_PID ! {ping, self()},
   receive
       pong ->
           io:format("Ping received pong~n", [])
   end,
   ping(N - 1, Pong_PID).

pong() ->

   receive
       finished ->
           io:format("Pong finished~n", []);
       {ping, Ping_PID} ->
           io:format("Pong received ping~n", []),
           Ping_PID ! pong,
           pong()
   end.

start() ->

   Pong_PID = spawn(tut15, pong, []),
   spawn(tut15, ping, [3, Pong_PID]).

1> c(tut15). {ok,tut15} 2> tut15: start(). <0.36.0> Pong received ping Ping received pong Pong received ping Ping received pong Pong received ping Ping received pong ping finished Pong finished

La función start primero crea un proceso llamado “pong”:

Pong_PID = spawn(tut15, pong, [])

Este proceso ejecuta tut15:pong(). Pong_PID es la identidad del proceso “pong”. La función start ahora, crea un nuevo proceso llamado “ping”.

spawn(tut15, ping, [3, Pong_PID]),

este proceso ejecuta

tut15:ping(3, Pong_PID)

<0.36.0> es el valor de retorno de la función start.

El proceso “pong” ahora realiza lo siguiente:

receive

   finished ->
       io:format("Pong finished~n", []);
   {ping, Ping_PID} ->
       io:format("Pong received ping~n", []),
       Ping_PID ! pong,
       pong()

end.

El contructor receive es usado para permitir que los procesos esperen mensajes de otros procesos. Tiene este formato:

receive

  pattern1 ->
      actions1;
  pattern2 ->
      actions2;
  ....
  patternN
      actionsN

end.

Nota: antes del end no se utiliza un “;”. Mensajes entre procesos son validos únicamente entre los tipos de datos establecidos por Erlang. Pueden ser lists, tuples, integers, atoms, pids etc. Cada proceso tiene su propia cola para los mensajes que recibe. Los nuevos mensajes que recibe, son puestos al final de la cola. Cuando un proceso ejecuta receive, el primer mensaje en la cola es emparejado con el primer patrón en receive, si esto concuerda, el mensaje es removido de la cola y las acciones correspondientes al patrón son ejecutadas. Sin embargo, si el primer patrón no concuerda, el segundo patrón es probado, si este concuerda, el mensaje es removido de la cola y las acciones correspondientes al segundo patrón son ejecutadas. Si el segundo patrón no concuerda, el tercero es probado, etc. Así será hasta que ya no haya más patrones por probar. Si ya no existen más patrones por probar, el primer mensajes es retenido en la cola y se intenta con el segundo mensaje. Si este concuerda cualquier patrón, las acciones apropiadas son ejecutadas y el segundo mensaje es removido de la cola (manteniendo el primer y el resto de los mensajes en la cola). Si el segundo mensaje no concuerda se prueba el tercer mensaje, etc. Asi será hasta que se llegue al final de la cola. Si se llega al final de la cola, los procesos detienen su ejecucion y esperan hasta que un Nuevo mensaje sea recibido, este procedimiento de repite. La implementación en Erlang es “ingeniosa” y minimize el número de veces que cada mensaje es probado contra los patrones en receive. Continuando con el ejemplo de Ping-Pong: "Pong" esta esperando mensajes. Si el atom finished es recibido, “pong” imprime “pong finished” y como no tiene nada mas que hacer, finalize. Si recibe un mensaje en este formato: {ping, Ping_PID} Imprime "Pong received ping" y envia el atom pong al proceso "ping": Ping_PID ! pong

Nota: El operador “!” es utilizado para enviar mensajes. Su sintaxis es la siguiente: Pid ! Message En este caso Message (cualquier termino en Erlang) es enviado al proceso con identidad Pid. Después de enviar el mensaje pong, al proceso “ping”, “pong” llama a la función pong de nuevo, lo que causa regresar a receive otra vez y esperar otro mensaje. Ahora el proceso “ping” fue iniciado ejecutando: tut15:ping(3, Pong_PID) Viendo la función ping/2, observamos que la segunda clausula de ping/2 es ejecutada ya que el valor del primer argumento es 3 (no 0)(la primera clausula es ping(0,Pong_PID), segunda clausula es ping(N,Pong_PID), por consiguiente N se convierte en 3). La segunda clausula envía un mensaje a “pong”: Pong_PID ! {ping, self()}, self() retorna el pid del proceso y esto ejecuta self(), en este caso el pid de “ping”. (En el código de “pong” esto caerá en la variable Ping_PID en receive). "Ping" ahora deberá esperar por la respuesta de “pong”: receive

   pong ->
       io:format("Ping received pong~n", [])

end, y escribe "Ping received pong" cuando esta respuesta haya llegado, después de esto “ping” llama a la función ping una vez más. ping(N - 1, Pong_PID) N-1 causa que el primer argumento sea disminuido hasta llegar a 0. Cuando esto ocurre, la primera clausula de ping/2 será ejecutada: ping(0, Pong_PID) ->

   Pong_PID !  finished,
   io:format("ping finished~n", []);

El atom finished es enviado a "pong" (causando que el proceso termine) y se imprime "ping finished" en la ventana. "Ping" al no tener nada que hacer se finaliza.

Registrando el Nombre de Procesos[editar]

En el ejemplo anterior, primero se creo "pong" para poder darle la identidad de “pong” a “ping” al ser iniciada. En otras palabras, “ping" debe saber la identidad de “pong” para poder enviarle algún mensaje. Algunas veces los procesos que necesitan saber la identidad de otros son iniciados completamente independientes uno del otro. Erlang provee mecanismos para dale nombres a los procesos y así utilizar estos nombres como identidades en vez de los pids. Esto se hace utilizando la BIF register: register(some_atom, Pid)

Ahora, si reescribimos el ejemplo de Ping-Pong utilizando esto y dandole el nombre pong al proceso "pong": -module(tut16).

-export([start/0, ping/1, pong/0]).

ping(0) ->

   pong ! finished,
   io:format("ping finished~n", []);

ping(N) ->

   pong ! {ping, self()},
   receive
       pong ->
           io:format("Ping received pong~n", [])
   end,
   ping(N - 1).

pong() ->

   receive
       finished ->
           io:format("Pong finished~n", []);
       {ping, Ping_PID} ->
           io:format("Pong received ping~n", []),
           Ping_PID ! pong,
           pong()
   end.

start() ->

   register(pong, spawn(tut16, pong, [])),
   spawn(tut16, ping, [3]).

2> c(tut16). {ok, tut16} 3> tut16:start(). <0.38.0> Pong received ping Ping received pong Pong received ping Ping received pong Pong received ping Ping received pong ping finished Pong finished

En la función start/0, register(pong, spawn(tut16, pong, [])),

ambos generan el proceso “pong” y les da el nombre pong. En el proceso “ping”, ahora, se puede enviar mensajes a pong de esta manera: pong ! {ping, self()},

y así ping/2 ahora se convierte en ping/1 ya que, no debemos utilizar el argumento Pong_PID.

Manejo de Excepciones[editar]

Mecanismos de excepciones: catch y throw[editar]

throw(expresión) evalúa la expresión y proporciona el resultado al catch más cercano. Puede usarse para generar un mensaje de error. (catch expresión) evalúa la expresión, y si captura un mensaje de error genera una estructura de datos, bien la generada por throw o la generada por el propio sistema. Los mensajes de error proporcionados por el sistema suelen ser bastante descriptivos:

  • badarg si se le pasa un argumento incorrecto a una función,
  • badarith si se intenta hacer una operación con argumentos inadecuados,
  • badmatch si se intenta concordar con un patrón que no encaja,
  • case_clause si ninguna cláusula en un case sirve para un argumento dado...

Programación defensiva[editar]

Hay dos formas de corregir errores provocados por la entrada de datos incorrectos en el sistema:

  • Comprobar los datos antes de usarlos.
  • No comprobar los datos, sino recuperarse cuando ocurre un error.

La prevención de errores añade código poco útil para el sistema, incrementando el tamaño y la complejidad del mismo. Si la aparición de errores es infrecuente, es preferible no desperdiciar tiempo comprobando cada dato, ahorrando así tiempo en el caso frecuente de que no haya errores.

Reemplazo de código en caliente[editar]

La unidad mínima de reemplazo de código es el módulo. Dos versiones distintas del mismo módulo pueden estar cargadas en memoria al mismo tiempo. Cuando se realiza una llamada a una función se utiliza la última versión disponible del modulo.

Catch and Throw[editar]

Devuelve el valor de expr a menos que se produce una excepción durante la evaluación. En ese caso, se detecta la excepción. Para las excepciones de error de la clase, es decir, errores de ejecución: ( 'EXIT', (Razón, Stack)) es devuelto. Para las excepciones de la salida de clase, que es el código de llamada de salida (término): ( 'EXIT', término) es devuelto. Para las excepciones de la clase de tiro, que es el código de llamada tiro (term): Término se devuelve. Razón depende del tipo de error que se produjo, y la pila es la pila de llamadas a funciones más recientes, ver los errores y tratamiento de errores.

Tenga en cuenta que tiene una baja prioridad de capturas y capturas subexpresiones a menudo tiene que ser encerrado en un bloque o exprecion en paréntesis:

El BIF tiro (Cualquiera) puede ser utilizado para la retención local de una función. Debe ser evaluado dentro de una captura, que devolverá el valor Any. Ejemplo:

Tenga en cuenta que aunque la captura de palabras clave se utiliza en la expresión de tratar, no es una expresión de captura dentro de la expresión intentarlo.

Try[editar]

Devuelve el valor de Exprs (una secuencia de expresiones Expr1, ..., exprn) a menos que se produce una excepción durante la evaluación. En ese caso la excepción es capturada y los patrones de ExceptionPattern con el derecho de clase de excepción de clase son de forma secuencial compara con la excepción capturada. Un omitirse la clase es la abreviatura de tiro. Si un partido tiene éxito y la secuencia opcional de la Guardia ExceptionGuardSeq es cierto, la ExceptionBody correspondiente se evalúa a convertirse en el valor de retorno.

Si se produce una excepción durante la evaluación de Exprs pero no hay ExceptionPattern congruencia de la clase de derecho con una secuencia de la guardia real, la excepción se pasa como si Exprs no se había encerrado en una expresión de intentarlo. Si se produce una excepción durante la evaluación de ExceptionBody no es capturado.

 try Exprs
 catch
   [nowiki]   [Class1 :]ExceptionPattern1 [when ExpresionGuard1] -> [/nowiki]
                        ...ExceptionBody1;
   [nowiki]   [Class1 :]ExceptionPatternN [when ExpresionGuardN] -> [/nowiki]
                        ExceptionBodyN
 end

Gramática en EBNF del lenguaje[editar]

Características específicas del Lenguaje[editar]

Constructores de Alto Nivel[editar]

Erlang es un lenguaje declarativo. Los Lenguajes declarativos trabajan sobre el principio de tratar para describir lo que debe ser computado, en lugar de decir cómo se calcula este valor. Una definición de función-particularmente uno que utiliza un patrón de coincidencia (pattern matching) para seleccionar entre diferentes casos, y para extraer los componentes de las estructuras complejas de datos, leerán como un set de ecuaciones.

Procesos concurrentes y paso de mensaje[editar]

Concurrencia en Erlang es fundamental para su éxito. En lugar de proporcionar hilos de que comparten memoria, cada proceso de Erlang se ejecuta en su propio espacio de memoria y en su propia heap y stack. Los procesos no pueden interferir unos con otros sin darse cuenta, al igual que todos es fácil en modelado de hilos, dando lugar a bloqueos y otros horrores.

Los procesos se comunican entre sí mediante el paso de mensajes, donde el mensaje puede ser cualquier valor de dato Erlang en absoluto. El paso de mensajes es asincrónica, por lo que una vez que se envía un mensaje, el proceso puede continuar procesando. Los mensajes se recuperan del proceso de buzón de correo selectivamente, por lo que no es necesario procesar los mensajes en el orden en que se reciben. Esto hace que la concurrencia de más robusta, en particular cuando los procesos son distribuidos a través de diferentes equipos y el orden en que se reciben los mensajes dependerá de las condiciones de ambiente la red. La Figura 1-1 muestra un ejemplo, donde un "servidor de la zona" procesa calcular áreas de formas para un cliente.

Escalable, segura y concurrencia eficiente[editar]

La Concurrencia de Erlang es rápida y escalable. Sus procesos son de peso ligero en el que la máquina virtual de Erlang no crea un subproceso de sistema operativo para cada proceso creado. Se crean, programado, y manipulados en la máquina virtual, independiente del sistema operativo subyacente. Como resultado, el tiempo de proceso de creación es del orden de microsegundos e independiente del número de procesos existentes simultáneamente. Los procesos de Erlang se comunican entre sí a través del paso de mensajes. A pesar de del número de procesos concurrentes en el sistema, el intercambio de mensajes dentro de la sistema le toma microsegundos. Todo lo que está involucrado en el paso de mensajes es la copia de los datos del espacio de memoria de un proceso hacia el espacio de memoria del otro, todo dentro de la misma máquina virtual.

Propiedades livianas en tiempo real[editar]

A pesar de que Erlang es un lenguaje de alto nivel, se puede usar para tareas con limitaciones livianas en tiempo real. La gestión del almacenamiento en Erlang es automatizado, con la recolección de basura en práctica en función de cada proceso. Esto le da al sistema un tiempo de respuesta en el orden de milisegundos, incluso en la presencia de basura recolectada en la memoria. Debido a esto, Erlang puede manejar grandes cargas sin degradación en el rendimiento, incluso durante los picos de sostenido.

Robustez[editar]

¿Cómo se construye un sistema robusto? Aunque Erlang no puede resolver todos sus problemas, facilitará en gran medida su tarea en una fracción del esfuerzo de otros lenguages de programación. Gracias a una serie de simples pero potentes mecanismos de control de errores y del seguimiento de los constructores de excepciones, una biblioteca de módulos muy general se han construido, diseñado con robustez en su núcleo. Al programar para el caso correcto y dejando que estas bibliotecas manejen los errores, no sólo son programas más cortos y fáciles de entender, pero usualmente contienen menos errores.

Coincidencia de patrones (Pattern Matching)[editar]

Coincidencia de patrones en Erlang se usa para:

  • Asigna valores a las variables
  • Control del flujo de ejecución de los programas
  • Extraer valores de tipos de datos compuestos

La combinación de estas características permite escribir conciso, legible y potente

programas, sobre todo cuando la coincidencia de patrones se usa para manejar los argumentos de una función que se está definiendo. El patrón de concordancia se escribe así: 

Pattern = Expression

Y como dijimos antes, es una generalización de lo que ya vimos cuando hablamos acerca de las variables. El patrón se compone de estructuras de datos que puede contener tanto variables dependientes y no dependientes, así como valores literales (como los átomos, enteros o cadenas). Una variable de la dependiente es una variable que ya tiene un valor, y una variable independiente es una que todavía no depende de un valor. Ejemplos de patrones incluyen:


Double {Double, 34} {Double, Double} [true, Double, 23, {34, Treble}]

La expresión se compone de estructuras de datos, variables dependientes, operaciones matemáticas, y llamadas a funciones. No puede contener valores independientes.

¿Qué sucede cuando se realiza una comparación de patrones? Dos resultados posibles:

  • El patrón de concordancia puede tener éxito, y esto resulta en las variables independientes se convierten en dependientes (y el valor de la expresión que se retorna).
  • El patrón de concordancia pueden fallar, y ninguna variable se convierte en dependiente como consecuencia de ello.

Ejemplos Pequeños del Lenguaje[editar]

Para entender mejor el lenguaje que mejor manera que con los ejemplos comunes de todos los lenguajes. Y así notar la diferencia entre Erlang y el resto.

factorial.erl[editar]

     -module(factorial).
     -export([factorial/1]).
     
     factorial(N) when N == 0 -> 1;
     factorial(N) when N > 0 -> N * factorial(N-1).
     1> c(factorial).
       {ok,factorial}
     2> factorial:factorial(20).
       2432902008176640000
     3> 

Lo importante de este caso es ver el modo como funciona factorial en Erlang. Las comparaciones que se realizan en Erlang son descritas en la siguiente tabla:

Operador Descripción Tipo
X > Y X es mayor que Y Coerce
X < Y X es menor que Y Coerce
X =< Y X es igual o menor que Y Coerce
X >= Y X es igual o mayor que Y Coerce
X == Y X es igual a Y Coerce
X /= Y X no es igual a Y Coerce
X =:= Y X es igual a Y Exact
X =/= Y X no es igual a Y Exact

La diferencia entre ‘Exact’ y ‘Coerce’ es inmensa. Por ejemplo en ‘Coerce’ cuando comparamos 2 números solo compara magnitudes tal como 5.0 == 4+1. Sin embargo ‘Exact’ lo que hace es decir que no son iguales porque 5.0 no es igual a 5 ya que un entero no es similar a un punto flotante.

Los casos tipos if, else y case se verán aplicados al mismo factorial para entender la diferencia.

Tipo If y Else[editar]

   -module(factorial2).
   -export([factorial/1]).
   
   factorial(N) ->
        if
                N == 0 -> 1;
                N > 0 -> N*factorial(N-1)
        end.

Si se puede observar esto es como condición instrucción terminada en ‘;’ excepto la condición final.

Tipo Case[editar]

   -module (factorial3).
   -export ([factorial/1]).
   
   
   factorial(N) ->
       case N of
            0 -> 1;
            N when N > 0 ->
                N * factorial(N - 1)
       end.

Si se observa de este código tenemos que podemos hacer condiciones dentro del mismo case. Para demostrar un programa fácil pero con un poco más de complejidad haremos el cifrado de César con rotación 13.

Cifrado Cesar (Rotación 13)[editar]

El cifrado de cesar con rotación 13 mueve una letra 13 posiciones más adelante.

  -module (cesar).
  -export ([cifrar/1, descifrar/1]).
  
  cifrar(String) -> 
        list_to_atom([X+13 || X <- String]).
  
  descifrar(String) ->
        list_to_atom([X-13 || X <- String]).


Al compilar:

 1> c(cesar).
    {ok, cesar}
 2> cesar:cifrar("hola").
    'u|yn'
 3> cesar:descifrar("u|yn").
    hola

Lo que aquí se aprecia es un nuevo tipo de instrucción. EL cual Erlang permite usar una lista y aplicarle una función a todos los elementos de la lista. De ahí la instrucción list_to_atom devuelve la lista de enteros como una lista de caracteres es decir una cadena.

Apéndice A (¿Cómo Descargar Erlang?)[editar]

Windows[editar]

Los pasos para instalar erlang son los siguientes:

  • Descargar el archivo: "Página de Erlang"
  • Al descargarlo, ejecutarlo inmediatamente
  • Seguir las instrucciones en el instalador
  • Ejecutar el programa

Otros Sistemas Operativos[editar]

Los pasos son los siguientes:

  • Deben ir a la página: "Página de Erlang(2)"
  • Descargar el código fuente de ser necesario y compilarlo.
  • Descargar la documentación de la página
  • Asegurarse de descargar los archivos del mismo tipo.

Fuentes[editar]

Armstrong, J., Virding, R., Wikström, C., & Williams, M. (1996). Concurrent Programming in ERLANG. Älvsjö: Prentice Hall.

Feely, M. (s.f.). CiteSeerX. Recuperado el 17 de Septiembre de 2009, de CiteSeerX: A case for the unified heap approach to Erlang memory management (2001): "Heap Approach Erlang"

Wikipedia. (s.f.). Erlang Programming/Pattern Matching. Recuperado el 17 de Septiembre de 2009, de Wikipedia:Erlang Programming/Pattern Matching: "Pattern Matching"