Impresión de Estructuras Anidadas

Para imprimir una estructura de datos compleja existen varias soluciones. La mas común es usar el módulo Data::Dumper.

La Función dumpValue de dumpvar.pl

El módulo forma parte de la distribución de Perl desde la versión 5.5:

nereida:~> perl -MModule::CoreList -e \
   'print Module::CoreList->first_release("Data::Dumper")."\n"'
5.005

Si la versión de Perl con la que trabajamos es antigua, existe una solución se puede usar la función dumpValue que se encuentra en el fichero dumpvar.pl que se distribuye con la librería estandar. Sigue un ejemplo:

$ cat mydumpvalue.pl
#!/usr/bin/perl -w
use strict;

require 'dumpvar.pl';

my @a = (1, {A => ['AB', 'empty'], B => ['bB', 'empty']});

dumpValue(\@a); # recibe su argumento como una referencia
$ ./mydumpvalue.pl
0  1
1  HASH(0x8106af0)
   'A' => ARRAY(0x8103068)
      0  'AB'
      1  'empty'
   'B' => ARRAY(0x8106b68)
      0  'bB'
      1  'empty'

El Módulo Data::Dumper

El módulo Data::Dumper convierte la lista de escalares pasada como parámetro a su función Dumper en una cadena conteniendo código Perl que describe la estructura de datos.

nereida:~/alu/0607/alu2644> perl -wde 0
main::(-e:1):   0
  DB<1> use Data::Dumper
  DB<2> @a = (1, {A => ['AB', 'empty'], B => ['bB', 'empty']})
  DB<3> p Dumper(\@a)
$VAR1 = [
          1,
          {
            'A' => [
                     'AB',
                     'empty'
                   ],
            'B' => [
                     'bB',
                     'empty'
                   ]
          }
        ];
  DB<4> $Data::Dumper::Indent = 0
  DB<5> p Dumper(\@a)
$VAR1 = [1,{'A' => ['AB','empty'],'B' => ['bB','empty']}];
  DB<6> $Data::Dumper::Indent = 1
  DB<8> p Dumper(\@a)
$VAR1 = [
  1,
  {
    'A' => [
      'AB',
      'empty'
    ],
    'B' => [
      'bB',
      'empty'
    ]
  }
];
  DB<9> $Data::Dumper::Indent = 3
  DB<10> p Dumper(\@a)
$VAR1 = [
          #0
          1,
          #1
          {
            'A' => [
                     #0
                     'AB',
                     #1
                     'empty'
                   ],
            'B' => [
                     #0
                     'bB',
                     #1
                     'empty'
                   ]
          }
        ];



Subsecciones
Casiano Rodríguez León
2010-03-03
Y> --+ +--------------+ | refcount = 1 | | refcount = 1 | +--------------+ +--------------+ | o | | o | +---------- : -+ +---------- : -+ : : : : V : +--------------+ +--------------+ : | array | | hash ref | : +--------------+ +--------------+ : | refcount = 1 | | refcount = 0 | V +--------------+ +--------------+ +--------------+ | no elements | | o========> | hash | +--------------+ +--------------+ +--------------+ | refcount = 2 | +--------------+ +--------------+ | keys & vals | | hash ref | +--------------+ +--------------+ | refcount = 0 | +--------------+ +--------------+ | o========> | hash | +--------------+ +--------------+ | refcount = 1 | +--------------+ | keys & vals | +--------------+

Los contadores de referencias de los hashes alcanzan cero y son liberados. Ahora el contador del antiguo segundo elemento alcanza cero:

+--------------+                              +--------------+
| array ref    |                              | hash ref     |
+--------------+                              +--------------+
| refcount = 1 |                              | refcount = 1 |
+--------------+                              +--------------+
|           o  |                              |           o  |
+---------- : -+                              +---------- : -+
            :                                             :
            :                                             :
            V                                             :
+--------------+                                          :
| array        |                                          :
+--------------+                                          :
| refcount = 1 |                                          V
+--------------+                              +--------------+
| no elements  |                              | hash         |
+--------------+                              +--------------+
                                              | refcount = 1 |
                                              +--------------+
                                              | keys & vals  |
                                              +--------------+
                                             
                                             
                                              +--------------+
                                              | hash         |
                                              +--------------+
                                              | refcount = 0 |
                                              +--------------+
                                              | keys & vals  |
                                              +--------------+

y es liberado:

+--------------+                              +--------------+
| array ref    |                              | hash ref     |
+--------------+                              +--------------+
| refcount = 1 |                              | refcount = 1 |
+--------------+                              +--------------+
|           o  |                              |           o  |
+---------- : -+                              +---------- : -+
            :                                             :
            :                                             :
            V                                             :
+--------------+                                          :
| array        |                                          :
+--------------+                                          :
| refcount = 1 |                                          V
+--------------+                              +--------------+
| no elements  |                              | hash         |
+--------------+                              +--------------+
                                              | refcount = 1 |
                                              +--------------+
                                              | keys & vals  |
                                              +--------------+

Liberación de Memoria en Estructuras de Datos Cíclicas

Existe un problema con este algoritmo de recolección de basura: las referencias circulares. En esos casos el programador debe actuar usando, por ejemplo, el operador delete y las funciones weaken e isweak en el módulo Scalar::Util .

pp2@nereida:~$ perl -wde 0
main::(-e:1):   0
  DB<1> use Scalar::Util qw(weaken isweak)
  DB<2> $var = "hola\n"
  DB<3> $ref = \$var
  DB<4> weaken($ref)
  DB<5> p isweak($ref)
1
  DB<6> $c = $ref
  DB<7> p "<".isweak($c).">"
<>

La función weaken

La función weaken recibe como argumento una referencia. Hace que no se incremente el contador de referencias del valor asociado con dicha referencia. Vea el siguiente ejemplo:



hp@nereida:~/Lperl/src$ cat -n noweaken.pl
1  use strict;
2  use Scalar::Util qw(weaken);
3  my $ref;
4  {
5    my $var = "hola\n";
6    $ref = \$var;
7  }
8  print $$ref;
lhp@nereida:~/Lperl/src$ cat -n weaken.pl
 1  use strict;
 2  use Scalar::Util qw(weaken);
 3  my $ref;
 4  {
 5    my $var = "hola\n";
 6    $ref = \$var;
 7    weaken($ref);
 8  }
 9  print $$ref;
lhp@nereida:~/Lperl/src$ perl noweaken.pl
hola
hp@nereida:~/Lperl/src$ perl weaken.pl
Can't use an undefined value as a SCALAR reference ...


Una referencia se dice débil (weak) si no cuenta en el objeto al que referencia. Cuando el contador del objeto al que referencia alcanza cero la referencia contiene undef.

Tenga en cuenta que la copia de una referencia débil (creada con weaken) es una referencia fuerte:

lhp@nereida:~/Lperl/src$ cat -n weakenwithcopy.pl
 1  use strict;
 2  use Scalar::Util qw(weaken);
 3  my ($ref, $copy);
 4  {
 5    my $var = "hola\n";
 6    $ref = \$var;
 7    weaken($ref);
 8    $copy = $ref;
 9  }
10  print $$ref;
11
lhp@nereida:~/Lperl/src$ perl weakenwithcopy.pl
hola

La función isweak

La función isweak retorna TRUE si la expresión pasada como parámetro es una referencia débil.

Detectando Ciclos

El módulo Devel::Cycle permite la detección de ciclos:
lhp@nereida:~/Lperl/src/testing$ cat -n ciclos.pl
     1  #!/usr/local/bin/perl -w
     2  use strict;
     3  use Devel::Cycle;
     4  my $test = {
     5    fred   => [qw(a b c d e)],
     6    ethel  => [qw(1 2 3 4 5)],
     7    george => {martha => 23, agnes  => 19}
     8  };
     9  $test->{george}{phyllis} = $test;
    10  $test->{fred}[3]      = $test->{george};
    11  $test->{george}{mary} = $test->{fred};
    12  find_cycle($test);
    13  exit 0;
lhp@nereida:~/Lperl/src/t
Al ejecutar produce una salida como esta:
lhp@nereida:~/Lperl/src/testing$ ./ciclos.pl
Cycle (1):
                          $A->{'fred'} => \@B
                               $B->[3] => \%C
                          $C->{'mary'} => \@B

Cycle (2):
                          $A->{'fred'} => \@B
                               $B->[3] => \%C
                       $C->{'phyllis'} => \%A

Cycle (3):
                        $A->{'george'} => \%C
                          $C->{'mary'} => \@B
                               $B->[3] => \%C

Cycle (4):
                        $A->{'george'} => \%C
                       $C->{'phyllis'} => \%A

El módulo permite comprobar la existencia de ciclos con referencias débiles mediante la función find_weakened_cycle . El programa:

lhp@nereida:~/Lperl/src/testing$ cat -n ciclosweaken.pl
 1  #!/usr/local/bin/perl -w
 2  use strict;
 3  use Scalar::Util qw(weaken);
 4  use Devel::Cycle;
 5  my $test = {
 6    fred   => [qw(a b c d e)],
 7    ethel  => [qw(1 2 3 4 5)],
 8    george => {martha => 23, agnes  => 19}
 9  };
10  $test->{george}{phyllis} = $test;
11  $test->{fred}[3]      = $test->{george};
12  $test->{george}{mary} = $test->{fred};
13  weaken($test->{george}->{phyllis});
14  find_weakened_cycle($test);
15  exit 0;

Produce la salida:

lhp@nereida:~/Lperl/src/testing$ ciclosweaken.pl
Cycle (1):
                          $A->{'fred'} => \@B
                               $B->[3] => \%C
                          $C->{'mary'} => \@B

Cycle (2):
                          $A->{'fred'} => \@B
                               $B->[3] => \%C
                   w-> $C->{'phyllis'} => \%A

Cycle (3):
                        $A->{'george'} => \%C
                          $C->{'mary'} => \@B
                               $B->[3] => \%C

Cycle (4):
                        $A->{'george'} => \%C
                   w-> $C->{'phyllis'} => \%A

El Módulo Devel::Peek

Estudie la siguiente ejecución con el depurador. El módulo Devel::Peek nos permite observar la estructura interna de las variables en el intérprete:

lhp@nereida:~/Lperl/src$ perl -MDevel::Peek -wd weakenwithcopy.pl
main::(weakenwithcopy.pl:3):    my ($ref, $copy);
main::(weakenwithcopy.pl:4):    {
  DB<1> c 10
main::(weakenwithcopy.pl:10):   print $$ref;
  DB<2> -
1:      use strict;
2:      use Scalar::Util qw(weaken);
3:      my ($ref, $copy);
4       {
5:        my $var = "hola\n";
6:        $ref = \$var;
7:        weaken($ref);
8:        $copy = $ref;
9       }
10==>   print $$ref;
  DB<2> Dump $ref
SV = RV(0x817b328) at 0x823a050
  REFCNT = 2
  FLAGS = (PADBUSY,PADMY,ROK,WEAKREF)
  RV = 0x814f7e0
  SV = PVMG(0x83dcb80) at 0x814f7e0
    REFCNT = 1
    FLAGS = (PADBUSY,PADMY,RMG,POK,pPOK)
    IV = 0
    NV = 0
    PV = 0x8238fa8 "hola\n"\0
    CUR = 5
    LEN = 8
    MAGIC = 0x83e3368
      .............................
  DB<3> Dump $copy
SV = RV(0x817b324) at 0x814f810
  REFCNT = 2
  FLAGS = (PADBUSY,PADMY,ROK)
  RV = 0x814f7e0
  SV = PVMG(0x83dcb80) at 0x814f7e0
    REFCNT = 1
    FLAGS = (PADBUSY,PADMY,RMG,POK,pPOK)
    IV = 0
    NV = 0
    PV = 0x8238fa8 "hola\n"\0
    CUR = 5
    LEN = 8
    MAGIC = 0x83e3368
      .............................

Contadores de Referencias de Listas

El siguiente ejemplo muestra como los elementos de un array tienen sus propios contadores de referencias:

DB<1> @a = 1..5
DB<2> $ra = \$a[4]
DB<3> pop @a
DB<4> p @a
1234
DB<5> p $$ra
5
la orden pop @a rompe la ligadura entre $a[4] y el valor, esto es, la ligadura-variable $a[4] es eliminada del array. Sin embargo el valor asociado no es eliminado de memoria ya que su contador de referencias todavía no es cero. La variable $ra aún esta referenciándolo.



Subsecciones
Casiano Rodríguez León
2011-02-09