Skip to content

Latest commit

 

History

History
733 lines (559 loc) · 27.3 KB

perldsc.pod

File metadata and controls

733 lines (559 loc) · 27.3 KB

NAME

perldsc - Introducción a las estructuras de datos

DESCRIPCIÓN

La única característica con más carencias que tenía el lenguaje de programación Perl antes de la versión 5.0 eran las complejas estructuras de datos. Incluso sin tener soporte directo en el lenguaje, algunos valientes programadores se las arreglaron para emularlas, pero era un trabajo duro y no para los débiles de corazón. De vez en cuando se podía solventar este asunto con la notación $m{$AoA,$b} tomado del awk en el que las claves son en realidad más como una sola cadena concatenada "$AoA$b", pero el recorrido y ordenación eran muy dificultosos. Los programadores más desesperados incluso hackearon la tabla de símbolos internos de Perl, directamente, una estrategia que demostró ser difícil de desarrollar y mantener -por decirlo suavemente-.

La versión 5.0 de Perl nos permite tener complejas estructuras de datos. Ahora puede escribir algo como lo siguiente, y de repente, ¡tiene una matriz de tres dimensiones!

for $x (1 .. 10) {
        for $y (1 .. 10) {
            for $z (1 .. 10) {
                $AoA[$x][$y][$z] =
                    $x ** $y + $z;
            }
        }
}

Sin embargo, tan simple que puede parecer esto, ¡esconde una construcción mucho más elaborada de lo que el ojo ve!

¿Cómo se imprime? ¿Por qué no podemos decir print @AoA? ¿Cómo se ordena? ¿Cómo se puede pasar a una función u obtener uno de estas construcciones desde una función? ¿Es un objeto? ¿Se puede guardar en disco para leerlo más tarde? ¿Cómo accedes a las filas o columnas de esta matriz? ¿Todos los valores tienen que ser numéricos?

Como puede ver, es muy fácil quedar confuso. Mientras que una pequeña porción de la culpa de esto se puede atribuir a la implementación basada en referencias, en realidad es más debido a la falta de documentación existente con ejemplos diseñados para el principiante.

Este documento intenta ser un detallado pero entendible tratamiento de las diferentes maneras con las que puede trabajar con estructuras de datos. También debería servir como libro de recetas con ejemplos. De esta manera, cuando necesite crear una estructura compleja de datos, puede pellizcar, robar, hurtar, o sustraer alguno de los ejemplos que estén por aquí.

Echemos un vistazo a cada una de estas construcciones posibles en detalle. Hay separaciones separada para cada una de las siguientes:

  • arrays de arrays

  • hashes de arrays

  • arrays de hashes

  • hashes de hashes

  • construcciones más elaboradas

Pero por ahora, miremos los problemas genéricos a todos estos tipos de estructuras de datos.

REFERENCIAS

La cosa más importante que hay que entender sobre todas las estructuras de datos en Perl -incluyendo los arrays multidimensionales- es que incluso aunque no lo parezcan, los @ARRAYs y %HASHes de Perl, son todos, internamente, unidimensionales. Sólo pueden almacenar valores escalares (es decir, una cadena, un número o un referencia). No pueden contener, directamente, otros arrays o hashes, sino que, en su lugar, contienen referencias a otros arrays o hashes.

No puede utilizar una referencia a un array o a un hash de la misma forma que lo haría con un array o un hash real. Para los programadores de C o C++ acostumbrados a distinguir entre arrays y punteros a ellos, esto puede resultarles algo confuso. Si es así, sólo piense en ello como la diferencia que hay entre una estructura y un puntero a una estructura.

Usted puede (y debe) leer más acerca de las referencias en perlref. En pocas palabras, las referencias son como punteros que saben a lo que apunta. (Los objetos son también un tipo de referencia, pero no vamos a necesitarles de inmediato -si acaso-). Esto significa que cuando usted tiene algo que se parece a un acceso a un array y/o hash de dos o más dimensiones, lo que realmente está sucediendo es que el tipo básico no es más que una entidad de una dimensión que contiene referencias al siguiente nivel. Es sólo que puede usarlo como si se tratara de una entidad bidimensional. Esto es, en realidad, casi de la misma forma en que funcionan casi todas las matrices multidimensionales en C.

$array[7][12]                   # array de arrays
$array[7]{cadena}                       # array de hashes
$hash{cadena}[7]                        # hash de arrays
$hash{cadena}{'otra cadena'}    # hash de hashes

Ahora, debido a que el nivel superior sólo contiene referencias, si trata de imprimir su array con una simple función print(), obtendrá algo que no se ve muy bien, algo como esto:

@AoA = ( [2, 3], [4, 5, 7], [0] );
print $AoA[1][2];
  7
print @AoA;
  ARRAY(0x83c38)ARRAY(0x8b194)ARRAY(0x8b1d0)

Esto es debido a que Perl no (nunca), implícitamente, desreferencia sus variables. Si lo que quiere es obtener aquello a lo que la referencia apunta, entonces debe hacerlo por sí mismo usando los prefijos indicadores de tipos, como ${$bla}, @{$bla}, @{$bla[$i]}, o usar los punteros flecha pos-fijas, como $a->[3], $h->{pedro}, o incluso $ob->metodo()->[3].

ERRORES COMUNES

Los dos errores más comunes en la construcción de algo así como un array de arrays, es tanto accidentalmente obtener el número de elementos, o bien tomar una referencia a la misma posición de memoria, de forma repetida. Aquí hay un ejemplo en el que usted obtiene el número de elementos en lugar de un array anidado:

for $i (1..10) {
        @array = algunafuncion($i);
        $AoA[$i] = @array;      # ¡ERROR!
}

Eso no es más que la sencilla asignación de un array a un escalar, obteniendo su número de elementos. Si eso es lo que real y verdaderamente desea hacer, entonces haría bien en considerar ser un poco más explícito al respecto, así:

for $i (1..10) {
        @array = algunafuncion($i);
        $cuantos[$i] = scalar @array;
}

Aquí está el caso de tomar una referencia a la misma ubicación de memoria, una y otra vez:

for $i (1..10) {
        @array = algunafuncion($i);
        $AoA[$i] = \@array;     # ¡ERROR!
}

Entonces, ¿dónde está el problema? Se ve bien, ¿no? Después de todo, te acabo de decir que necesitas un array de referencias, así que, mira por dónde, ¡me has dado uno!

Lamentablemente, si bien esto es cierto, sigue estando mal. Todas las referencias en @AoA se refieren a la misma posición, y por lo tanto, todos almacenan lo que el último @array contenía! Es similar al problema demostrado en el siguiente programa en C:

#include <pwd.h>
main() {
        struct passwd *getpwnam(), *rp, *dp;
        rp = getpwnam("root");
        dp = getpwnam("daemon");

        printf("nombre del demonio es %s\nnombre del root es %s\n",
                dp->pw_name, rp->pw_name);
}

que imprimirá

nombre del demonio es daemon
nombre del root es daemon

El problema es que tanto rp como dp son punteros ¡a la misma posición de memoria! En C, tiene que recordar que debe hacer un malloc() para reservar un poco de memoria. En Perl querrá usar, en su lugar, el constructor de array [] o el constructor de hash {}. Aquí está la forma correcta de proceder con el fragmento de código erróneo anterior:

for $i (1..10) {
        @array = algunafuncion($i);
        $AoA[$i] = [ @array ];
}

Los corchetes hacen referencia a un nuevo array con una copia de lo que está en @array en el momento de la asignación. Esto es lo que realmente quiere.

Tenga en cuenta que esto produce algo similar, pero es mucho más difícil de leer:

for $i (1..10) {
        @array = 0 .. $i;
        @{$AoA[$i]} = @array;
}

¿Es lo mismo? Bueno, quizás sí, quizás no. La diferencia sustancial es que cuando asigna algo con corchetes, está seguro que es siempre una nueva referencia con un nueva copia de los datos. Otra cosa podría estar pasando, en este nuevo caso, con la desreferencia @{$AoA[$i]} en el lado izquierdo de la asignación. Todo depende de si $AoA[$i] era indefinido en el momento de empezar, o si ya contenía una referencia. Si usted ya había poblado @AoA con referencias, como en

$AoA[3] = \@otro_array;

entonces la asignación con la indirección en el lado izquierdo podría usar la referencia actual que estaba allí:

@{$AoA[3]} = @array;

Naturalmente, esto podría tener el "interesante" efecto de sobreescribir @otro_array. (¿Alguna vez ha notado que cuando un programador dice que algo es "interesante" en lugar de decir "intrigante", inquietantemente quiere decir que es algo "molesto", "difícil", o ambas cosas?

Así que recuerde siempre utilizar constructores de array o hash con [] o {}, y todo irá bien, aunque no siempre sea óptimamente eficiente.

Sorprendentemente, la siguiente construcción, aparentemente peligrosa, se desenvolverá realmente bien:

for $i (1..10) {
    my @array = somefunc($i);
    $AoA[$i] = \@array;
}

Esto es así porque my() es más una sentencia en tiempo de ejecución que una declaración en tiempo de compilación. Esto significa que la variable my() es rehecha de nuevo cada vez que se reinicia el bucle. Así que, aunque parezca como si se almacenara cada vez la misma referencia a la misma variable, ¡en realidad no lo hace! Esta es una distinción sutil que puede generar un código más eficiente con el riesgo de inducir a error a todos los lectores excepto a los programadores más experimentados. Así que, por lo general, se desaconseja su enseñanza a los principiantes. De hecho, excepto para pasar argumentos a las funciones, raramente me gustaría ver el operador dame-una-referencia (barra diagonal inversa) a lo largo de todo el código. En su lugar, les aconsejo a los principiantes que ellos (y la mayoría de nosotros) debería tratar de utilizar los constructores, mucho más fáciles de entender, [] y {}, en lugar de confiar en contextos léxicos (o dinámicos) y en referencia ocultas, esperando que hagan las cosas correctas detrás del telón.

En resumen:

$AoA[$i] = [ @array ];  # generalmente, el mejor
$AoA[$i] = \@array;             # peligroso; ¿cómo era el my() de este array?
@{ $AoA[$i] } = @array; # demasiado complicado para la mayoría de los programadores

ADVERTENCIA SOBRE LA PRECEDENCIA

Hablando de cosas como @{$AoA[$i]}, los siguientes ejemplos son, realmente, lo mismo:

$aref->[2][2]   # claro
$$aref[2][2]    # confuso

¡Eso es porque las reglas de precedencia de Perl, con sus cinco prefijos de desreferencia (que se parecen a alguien jurando: $ @ * % &) tienen más prioridad que los subíndices con llaves o corchetes post-fijos! Esto, sin duda, será una gran sorpresa para el programador de C o C++, que está bastante acostumbrado a usar *a[i] para referirse al i-ésimo elemento de a. Es decir, primero toman el subíndice, y sólo entonces, desreferencian el elemento apuntado por ese subíndice. Eso está bien en C, pero esto no es C.

La construcción aparentemente equivalente en Perl, $$aref[$i], primero hace la desreferencia de $aref, tomando a $aref como una referencia a un array, y luego lo desreferencia para, finalmente, darnos el i-ésimo valor del array que apuntaba a $AoA. Si quiere seguir la misma notación que en C, tendría que escribir ${$AoA[$i]} para forzar a que $AoA[$i] sea evaluado antes del primer $ desreferenciador.

POR QUÉ DEBE SIEMPRE USAR use strict

Si esto está empezando a sonar más aterrador de lo que parece, relájese. Perl tiene algunas características que le ayudarán a evitar sus trampas más comunes. La mejor manera de evitar la confusión es comenzar cada programa como este de aquí:

#!/usr/bin/perl -w
use strict;

De esta forma, estará obligado a declarar todas las variables con my(), y también no permite que exista ninguna "desreferencia simbólica" accidental. Por lo tanto, si hubiera hecho:

my $aref = [
        [ "fred", "barney", "pebbles", "bambam", "dino", ],
        [ "homer", "bart", "marge", "maggie", ],
        [ "george", "jane", "elroy", "judy", ],
];

print $aref[2][2];

El compilador, de inmediato, marcaría esto como un error en tiempo de compilación, porque usted estaba, accidentalmente, accediendo a @aref, una variable no declarada, y por lo tanto le hace recordar que debe escribirlo así:

print $aref->[2][2]

DEBUGGING

Antes de la versión 5.002, el depurador estándar de Perl no hacía un buen trabajo a la hora de imprimir complejas estructuras de datos. A partir de la 5.002, el depurador incluye nuevas características, incluyendo la edición en línea de comandos, así como el comando x para volcar las complejas estructuras de datos. Por ejemplo, dada la asignación a $AoA de más arriba, aquí está la salida del depurador:

DB<1> x $AoA
$AoA = ARRAY(0x13b5a0)
   0  ARRAY(0x1f0a24)
          0  'pedro'
          1  'pablo'
          2  'pebbles'
          3  'bambam'
          4  'dino'
   1  ARRAY(0x13b558)
          0  'homer'
          1  'bart'
          2  'marge'
          3  'maggie'
   2  ARRAY(0x13b540)
          0  'george'
          1  'jane'
          2  'elroy'
          3  'judy'

EJEMPLOS DE CÓDIGO

Presentado con un pequeño comentario (que tendrán, algún día, su propia página de manual) aquí hay unos ejemplos de códigos cortos que ilustran el acceso a los distintos tipos de estructuras de datos.

ARRAYS DE ARRAYS

Declaración de un ARRAY DE ARRAYS

@AoA = (
       [ "pedro", "pablo" ],
       [ "george", "jane", "elroy" ],
       [ "homer", "marge", "bart" ],
     );

Generación de un ARRAY DE ARRAYS

# leyendo desde un archivo
while ( <> ) {
    push @AoA, [ split ];
}

# llamando a una función
for $i ( 1 .. 10 ) {
    $AoA[$i] = [ algunafuncion($i) ];
}

# usando variables temporales
for $i ( 1 .. 10 ) {
    @tmp = algunafuncion($i);
    $AoA[$i] = [ @tmp ];
}

# añadir a una fila actual
push @{ $AoA[0] }, "wilma", "betty";

Acceso e impresión de un ARRAY DE ARRAYS

# un elemento
$AoA[0][0] = "Pedro";

# otro elemento
$AoA[1][1] =~ s/(\w)/\u$1/;

# imprime todo usando referencias
for $aref ( @AoA ) {
    print "\t [ @$aref ],\n";
}

# imprime todo usando índices
for $i ( 0 .. $#AoA ) {
    print "\t [ @{$AoA[$i]} ],\n";
}

# imprime todo de golpe
for $i ( 0 .. $#AoA ) {
    for $j ( 0 .. $#{ $AoA[$i] } ) {
        print "elem. $i $j es $AoA[$i][$j]\n";
    }
}

HASHES DE ARRAYS

Declaración de un HASH DE ARRAYS

%HoA = (
       picapiedra         => [ "pedro", "pablo" ],
       jetsons            => [ "george", "jane", "elroy" ],
       simpsons           => [ "homer", "marge", "bart" ],
     );

Generación de un HASH DE ARRAYS

# leyendo desde un archivo
# picapiedra: pedro pablo wilma dino
while ( <> ) {
    next unless s/^(.*?):\s*//;
    $HoA{$1} = [ split ];
}

# leyendo desde un archivo; más temporales
# picapiedra: pedro pablo wilma dino
while ( $linea = <> ) {
    ($quien, $resto) = split /:\s*/, $linea, 2;
    @campos = split ' ', $resto;
    $HoA{$quien} = [ @campos ];
}

# llamando a una función que devuelve una lista
for $grupo ( "simpsons", "jetsons", "picapiedra" ) {
    $HoA{$group} = [ obtener_familia($grupo) ];
}

# lo mismo, pero usando temporales
for $grupo ( "simpsons", "jetsons", "picapiedra" ) {
    @miembros = obtener_familia($grupo);
    $HoA{$grupo} = [ @miembros ];
}

# añadir nuevos miembros a una familia actual
push @{ $HoA{"picapiedra"} }, "wilma", "betty";

Acceso e impresión de un HASH DE ARRAYS

# un elemento
$HoA{picapiedra}[0] = "Pedro";

# otro elemento
$HoA{simpsons}[1] =~ s/(\w)/\u$1/;

# imprimirlo todo
foreach $familia ( keys %HoA ) {
    print "$familia: @{ $HoA{$familia} }\n"
}

# imprime todo usando índices
foreach $familia ( keys %HoA ) {
    print "familia: ";
    foreach $i ( 0 .. $#{ $HoA{$familia} } ) {
        print " $i = $HoA{$familia}[$i]";
    }
    print "\n";
}

# imprime todo ordenado por el número de miembros
foreach $familia ( sort { @{$HoA{$b}} <=> @{$HoA{$a}} } keys %HoA ) {
    print "$familia: @{ $HoA{$familia} }\n"
}

# imprime todo ordenado por el número de miembros y por el nombre
foreach $familia ( sort {
                            @{$HoA{$b}} <=> @{$HoA{$a}}
                                        ||
                                    $a cmp $b
            } keys %HoA )
{
    print "$familia: ", join(", ", sort @{ $HoA{$familia} }), "\n";
}

ARRAYS DE HASHES

Declaración de un ARRAY DE HASHES

@AoH = (
       {
           Lider    => "pedro",
           Amigo    => "pablo",
       },
       {
           Lider    => "george",
           Esposa   => "jane",
           Hijo     => "elroy",
       },
       {
           Lider    => "homer",
           Esposa   => "marge",
           Hijo     => "bart",
       }
 );

Generación de un ARRAY DE HASHES

# leyendo desde un archivo
# formato: LIDER=pedro AMIGO=pablo
while ( <> ) {
    $reg = {};
    for $campo ( split ) {
        ($clave, $valor) = split /=/, $campo;
        $reg->{$clave} = $valor;
    }
    push @AoH, $reg;
}


# leyendo desde un archivo
# formato: LIDER=pedro AMIGO=pablo
# sin temporales
while ( <> ) {
    push @AoH, { split /[\s+=]/ };
}

# llamando a una función que devuelve una lista de parejas clave/valor, como
# "líder","pedro","hija","pebbles"
while ( %campos = obtenersiguientepareja() ) {
    push @AoH, { %campos };
}

# lo mismo, pero sin usar variables temporales
while (<>) {
    push @AoH, { obtenerparejas($_) };
}

# añadir una clave/valor a un elemento
$AoH[0]{mascota} = "dino";
$AoH[2]{mascota} = "pequeño ayudante de Santa Claus";

Acceso e impresión de un ARRAY DE HASHES

# un elemento
$AoH[0]{lider} = "pedro";

# otro elemento
$AoH[1]{lider} =~ s/(\w)/\u$1/;

# imprime todo usando referencias
for $href ( @AoH ) {
    print "{ ";
    for $rol ( keys %$href ) {
        print "$rol=$href->{$rol} ";
    }
    print "}\n";
}

# imprime todo usando índices
for $i ( 0 .. $#AoH ) {
    print "$i es { ";
    for $rol ( keys %{ $AoH[$i] } ) {
        print "$rol=$AoH[$i]{$rol} ";
    }
    print "}\n";
}

# imprime todo de golpe
for $i ( 0 .. $#AoH ) {
    for $rol ( keys %{ $AoH[$i] } ) {
        print "elem. $i $rol es $AoH[$i]{$rol}\n";
    }
}

HASHES DE HASHES

Declaración de un HASH DE HASHES

%HoH = (
       picapiedra => {
                lider     => "pedro",
                amigo     => "pablo",
       },
       jetsons     => {
                lider     => "george",
                esposa    => "jane",
                "su hijo" => "elroy",
       },
       simpsons    => {
                lider     => "homer",
                esposa    => "marge",
                hijo     => "bart",
        },
);

Generación de un HASH DE HASHES

# leyendo desde un archivo
# picapiedra: lider=pedro amigo=pablo esposa=wilma mascota=dino
while ( <> ) {
    next unless s/^(.*?):\s*//;
    $quien = $1;
    for $campo ( split ) {
        ($clave, $valor) = split /=/, $campo;
        $HoH{$quien}{$clave} = $valor;
    }


# leyendo desde un archivo; más temporales
while ( <> ) {
    next unless s/^(.*?):\s*//;
    $quien = $1;
    $reg = {};
    $HoH{$quien} = $reg;
    for $campo ( split ) {
        ($clave, $valor) = split /=/, $campo;
        $reg->{$clave} = $valor;
    }
}

# llamando a una función que devuelve un hash con una clave y un valor
for $grupo ( "simpsons", "jetsons", "picapiedra" ) {
    $HoH{$grupo} = { obtener_familia($grupo) };
}

# lo mismo, pero usando temporales
for $grupo ( "simpsons", "jetsons", "picapiedra" ) {
    %miembros = obtener_familia($grupo);
    $HoH{$grupo} = { %miembros };
}

# añadir nuevos miembros a una familia actual
%nuevos_miembros = (
    esposa  => "wilma",
    mascota => "dino",
);

for $que (keys %nuevos_miembros) {
    $HoH{picapiedra}{$que} = $nuevos_miembros{$que};
}

Acceso e impresión de un HASH DE HASHES

# un elemento
$HoH{picapiedra}{esposa} = "wilma";

# otro elemento
$HoH{simpsons}{lider} =~ s/(\w)/\u$1/;

# imprimirlo todo
foreach $familia ( keys %HoH ) {
    print "$familia: { ";
    for $rol ( keys %{ $HoH{$familia} } ) {
        print "$rol=$HoH{$familia}{$rol} ";
    }
    print "}\n";
}

# imprime todo de forma un poco más ordenada
foreach $familia ( sort keys %HoH ) {
    print "$familia: { ";
    for $rol ( sort keys %{ $HoH{$familia} } ) {
        print "$rol=$HoH{$familia}{$rol} ";
    }
    print "}\n";
}


# imprime todo ordenado por el número de miembros
foreach $familia ( sort { keys %{$HoH{$b}} <=> keys %{$HoH{$a}} } keys %HoH ) {
    print "$familia: { ";
    for $rol ( sort keys %{ $HoH{$familia} } ) {
        print "$rol=$HoH{$familia}{$rol} ";
    }
    print "}\n";
}

# establece un orden (rango) de ordenación por cada rol
$i = 0;
for ( qw(lider esposa hijo hija amigo mascota) ) { $rango{$_} = ++$i }

# ahora imprime todo ordenado por el número de miembros
foreach $familia ( sort { keys %{ $HoH{$b} } <=> keys %{ $HoH{$a} } } keys %HoH ) {
    print "$familia: { ";
    # e imprime estos de acuerdo al orden del rango
    for $rol ( sort { $rango{$a} <=> $rango{$b} }  keys %{ $HoH{$familia} } ) {
        print "$rol=$HoH{$familia}{$rol} ";
    }
    print "}\n";
}

REGISTROS MÁS ELABORADOS

Declaración de REGISTROS MÁS ELABORADOS

Aquí hay un ejemplo que muestra cómo crear y usar un registro cuyos campos pueden ser ordenados de diferentes formas:

$rec = {
         TEXT      => $cadena,
         SEQUENCE  => [ @valores_anteriores ],
         LOOKUP    => { %alguna_tabla },
         THATCODE  => \&alguna_funcion,
         THISCODE  => sub { $_[0] ** $_[1] },
         HANDLE    => \*STDOUT,
};

print $rec->{TEXT};

print $rec->{SEQUENCE}[0];
$ultimo = pop @ { $rec->{SEQUENCE} };

print $rec->{LOOKUP}{"clave"};
($primera_c, $primer_v) = each %{ $rec->{LOOKUP} };

$respuesta = $rec->{THATCODE}->($arg);
$respuesta = $rec->{THISCODE}->($arg1, $arg2);

# cuidado con los bloques de llaves extra alrededor de los identificadores de archivos
print { $rec->{HANDLE} } "una cadena\n";

use FileHandle;
$rec->{HANDLE}->autoflush(1);
$rec->{HANDLE}->print(" una cadena\n");

Declaración de un HASH DE REGISTROS COMPLEJOS

%TV = (
   picapiedra => {
       series   => "picapiedra",
       noches   => [ qw(lunes jueves viernes) ],
       miembros => [
           { nombre => "pedro",   rol => "líder",  edad => 36, },
           { nombre => "wilma",   rol => "esposa", edad => 31, },
           { nombre => "pebbles", rol => "hijo",   edad =>  4, },
       ],
   },

   jetsons     => {
       series   => "jetsons",
       noches   => [ qw(miércoles sábado) ],
       miembros => [
           { nombre => "george", rol => "líder",  edad  => 41, },
           { nombre => "jane",   rol => "esposa", edad  => 39, },
           { nombre => "elroy",  rol => "hijo",   edad  =>  9, },
       ],
    },

   simpsons    => {
       series   => "simpsons",
       noches   => [ qw(lunes) ],
       miembros => [
           { nombre => "homer", rol => "líder",  edad  => 34, },
           { nombre => "marge", rol => "esposa", edad  => 37, },
           { nombre => "bart",  rol => "hijo",   edad  => 11, },
       ],
    },
 );

Generación de un HASH DE REGISTROS COMPLEJOS

# leyendo desde un archivo
# es más fácil hacerlo teniendo en el archivo
# los datos en el formato en crudo mostrado antes.  perl es feliz
# de analizar complejas estructuras de datos si se declaran como datos, así que
# algunas veces es más fácil hacer esto que

# construirlo pieza a pieza
$reg = {};
$reg->{series} = "picapiedra";
$reg->{noches} = [ buscar_dias() ];

@miembros = ();
# asume que este archivo tiene una sintaxis campo=valor
while (<>) {
    %campos = split /[\s=]+/;
    push @miembros, { %campos };
}
$reg->{miembros} = [ @miembros ];

# ahora recuperemos todo el conjunto
$TV{ $reg->{series} } = $reg;

###########################################################
# ahora, es posible que desee crear interesantes campos adicionales que
# incluyan punteros hacia la misma estructura, así que si
# cambia una parte, cambia todo, como por ejemplo
# si quiere que un campo {hijos} sea una referencia
# a un array con los registros de los hijos, sin tener que duplicar
# los registros y sin tener problemas a la hora de actualizar.
###########################################################
foreach $familia (keys %TV) {
    $reg = $TV{$familia}; # puntero temporal
    @hijos = ();
    for $persona ( @{ $reg->{miembros} } ) {
        if ($persona->{rol} =~ /hijo|hija/) {
            push @hijos, $persona;
        }
    }
    # RECUERDE: ¡¡¡$reg y $TV{$familia} apuntan a los mismos datos!!!
    $reg->{hijos} = [ @hijos ];
}

# copio el array, pero el propio array contiene en sí mismo punteros
# a objetos no copiados. esto significa que si hace que Bart sea
# algo más viejo, por medio de

$TV{simpsons}{hijos}[0]{edad}++;

# entonces esto también cambiará
print $TV{simpsons}{miembros}[2]{edad};

# porque $TV{simpsons}{hijos}[0] y $TV{simpsons}{miembros}[2]
# los dos apuntan a la misma tabla hash anónima subyacente

# imprimirlo todo
foreach $familia ( keys %TV ) {
    print "la $familia";
    print " está durante @{ $TV{$familia}{noches} }\n";
    print "sus miembros son:\n";
    for $quien ( @{ $TV{$familia}{miembros} } ) {
        print " $quien->{nombre} ($quien->{rol}), de edad $quien->{edad}\n";
    }
    print "parece que $TV{$familia}{lider} tiene ";
    print scalar ( @{ $TV{$familia}{hijos} } ), " cuyos nombres son ";
    print join (", ", map { $_->{nombre} } @{ $TV{$familia}{hijos} } );
    print "\n";
}

Bases de datos con ataduras

No es fácil atar una estructura de datos de múltiples niveles (por ejemplo, un hash de hashes) a un archivo dbm. El primer problem es que todas, excepto las BD GDBM y Berkeley tienen limitaciones de tamaño; pero aparte de eso, también tiene problemas con cómo las referencias son representadas en disco. Un módulo experimental que intenta, parcialmente, resolver esto es el módulo MLDBM. Revise su sitio CPAN más cercano, como se describe en perlmodlib, para obtener el código fuente de MLDBM.

VEA TAMBIÉN

perlref, perllol, perldata, perlobj

AUTOR

Tom Christiansen <tchrist@perl.com>

Última actualización: Wed Oct 23 04:57:50 MET DST 1996

POD ERRORS

Hey! The above document had some coding errors, which are explained below:

Around line 4:

Non-ASCII character seen before =encoding in 'Introducción'. Assuming UTF-8