Programación en Erlang/Tipos de datos complejos y su representación

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

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.