No sé cómo llamar a este blog

El blog de alguien sin derecho a expresarse

Registros

Tercera entrada sobre Erlang en un día.

Nuestros nuevos conocidos, los registros, dan la respuesta a la siguiente pregunta: ¿Qué ocurre cuando tenemos que pasar mucha información de un lado a otro, pero sólo vamos a utilizar o modificar una parte de ella?

Hemos conocido ya a las tuplas, que son, en principio, la mejor manera de pasar varios datos relacionados entre sí.

Pusimos el ejemplo del coche:

1> MiCoche={"Ford", "Focus Tddi", 90}.
{"Ford","Focus Tddi",90}
2>

Es una tupla bastante sencilla. Pero puede que queramos almacenar más información; al fin y al cabo, hay muchas cosas que decir sobre un coche: ¿Qué cubicaje tiene? ¿Qué tipo de combustible usa? ¿De qué color es? ¿Cuántas plazas y puertas? ¿Cuál es su matrícula?

Para almacenar esa información, la tupla parece seguir siendo nuestra aliada. Veamos:

2> MiCoche2={"Ford", "Focus Tddi", 90, 1790, diesel, verde, 5, 5, "1234-ABC"}.
{"Ford","Focus Tddi",90,1790,diesel,verde,5,5,"1234-ABC"}
3>

Buff. Nuestra tupla se ha hecho bastante complicada. Para sacar cualquier dato, tenemos que escribir mucho:

3> {_, _, _, _, _, Color, _, _, _}=MiCoche2.
{"Ford","Focus Tddi",90,1790,diesel,verde,5,5,"1234-ABC"}
4> Color.
verde
5>

La cosa se complica aún más cuando queremos cambiar algún valor:

-module(coche1).
-export([pinta/2]).

pinta({Marca, Modelo, Potencia, Cubicaje, Combustible,
       _Color, Plazas, Puertas, Matricula}, Color) ->
  {Marca, Modelo, Potencia, Cubicaje, Combustible, Color, Plazas,
   Puertas, Matricula}.
5> c(coche1).
{ok,coche1}
6> coche1:pinta(MiCoche2, negro).
{"Ford","Focus Tddi",90,1790,diesel,negro,5,5,"1234-ABC"}
7>

Parece que vamos mal por ahí.

Es mejor, cuando una tupla se nos hace tan larga, trabajar con “registros”. Los registros vienen a ser tuplas a cuyos campos se les ha asignado un nombre. No podemos definir directamente un registro en el shell, así que vamos a tener que crear un fichero para almacenar la definición de nuestro registro “coche”.

De paso, presentamos a otro personaje: los ficheros de cabecera. En Erlang, los ficheros de cabecera tienen la extensión “.hlr” y se utilizan, casi exclusivamente, para almacenar definiciones de registros. Así que creamos el fichero “coche.hlr”:

-record(coche,
  {marca, modelo, potencia, cubicaje, combustible,
   color, plazas, puertas, matricula}).

Volvamos al shell. He dicho que no podemos definir un registro en él y es cierto, pero podemos cargar la definición de un registro desde un fichero:

Erlang (BEAM) emulator version 5.6  [smp:2] [async-threads:0] [kernel-poll:false]

Eshell V5.6  (abort with ^G)
1> rr("coche.hrl").
[coche]
2>

Nótese que la función rr (Read Records) nos ha devuelto una lista con el átomo “coche”, que es el nombre del único registro definido en “coche.hrl”. Podemos definir más de un registro en el mismo fichero y, al contrario que los módulos, no se exige ningún nombre especial al fichero.

Vamos a volver a declarar mi coche:

2> MiCoche=#coche{marca="Ford", modelo="Focus Tddi", potencia=90, cubicaje=1790,
2> combustible=diesel, color=verde, puertas=5, plazas=5, matricula="1234-ABC"}.
#coche{marca = "Ford",modelo = "Focus Tddi",potencia = 90,
       cubicaje = 1790,combustible = diesel,color = verde,
       plazas = 5,puertas = 5,matricula = "1234-ABC"}
3>

Lo sé. Dije que los registros iban a simplificar el problema y parece que lo han complicado más. Por otro lado, ahora sabemos qué es cada campo y podríamos declarar los campos en el orden que quisiéramos, incluso omitir alguno (en cuyo caso tomarán como valor el átomo “undefined”). Además, podemos hacer trampa cuando creamos una instancia de un registro, como indicaré más adelante.

Veamos qué ocurre ahora cuando queremos sacar el color del coche:

3> #coche{color=Color}=MiCoche.
#coche{marca = "Ford",modelo = "Focus Tddi",potencia = 90,
       cubicaje = 1790,combustible = diesel,color = verde,
       plazas = 5,puertas = 5,matricula = "1234-ABC"}
4> Color.
verde
5>

Mucho más simple y no nos exige sabernos de memoria el orden de los campos. ¿Cómo queda ahora nuestra función de pintura?

-module(coche2).
-export([pinta/2]).
-include("coche.hrl").

pinta(Coche, Color) ->
  Coche#coche{color=Color}.
5> c(coche2).
{ok,coche2}
6> coche2:pinta(MiCoche, negro).
#coche{marca = "Ford",modelo = "Focus Tddi",potencia = 90,
       cubicaje = 1790,combustible = diesel,color = negro,
       plazas = 5,puertas = 5,matricula = "1234-ABC"}
7>

Resumiendo:

  • Definimos una instancia de un registro con #registro{campo=valor,...}
  • Extraemos el valor de uno o más campos con #registro{campo=Variable,...}
  • Obtenemos una versión modificada de una instancia de un registro con Variable#registro{campo=nuevoValor,...}

Cuando definimos un registro, podemos asignar valores por defecto a los campos. Así, por ejemplo, si queremos asumir, cuando no se especifique, que un coche es diesel y blanco, definiríamos el registro coche como

-record(coche,
  {marca, modelo, potencia, cubicaje,
   combustible=diesel, color=blanco, plazas,
   puertas, matricula}).

Los registros son muy utilizados en Erlang. En particular, el gestor de base de datos incorporado, Mnesia, utiliza tablas en las que cada fila es un registro. O, por ejemplo, YAWS, el servidor HTTP basado en Erlang, pasa a las aplicaciones el registro documentado aquí (que, a su vez, tiene registros y listas de registros como campos).

Internamente, los registros se almacenan como tuplas de la forma

{nombreRegistro, Valor1, Valor2,...}

así que nada nos impide, como dije antes, hacer trampa y declarar nuestro coche como una tupla:

Erlang (BEAM) emulator version 5.6  [smp:2] [async-threads:0] [kernel-poll:false]

Eshell V5.6  (abort with ^G)
1> rr("coche.hrl").
[coche]
2> MiCoche={coche, "Ford", "Focus Tddi", 90, 1790, diesel, verde, 5, 5, "1234-ABC"}.
#coche{marca = "Ford",modelo = "Focus Tddi",potencia = 90,
       cubicaje = 1790,combustible = diesel,color = verde,
       plazas = 5,puertas = 5,matricula = "1234-ABC"}
3>

Archivado bajo:Erlang ,

Leave a Reply