Perl Brasil

Pesquisar

Documentação

Planeta

Eventos

Comunidade

r7 - 31 May 2008 - MarcoLima


NOME

perldsc - Perl Data Structures Cookbook (estruturas de dados complexas)

voltar para o topo


DESCRIÇÃO

A mais importante funcionalidade que infelizmente faltava em Perl, até a versão 5.0, era a capacidade de lidar com estrutura de dados complexas. Mesmo sem suporte direto na linguagem, alguns bravos programadores conseguiram emular seu funcionamento, mas era um trabalho árduo e não era para os de coração fraco. Você poderia ocasionalmente se virar com a notação $m{$AoA,$b}, emprestada de awk, e, que as chaves eram a string concatenada "$AoA$b", mas transversar e ordenar era difícil. Programadores mais desesperados chegaram a hackear a tabela de símbolos de Perl diretamente, uma estratégia que se mostrava difícil de desenvolver e manter -- para dizer o mínimo. A versão 5.0 de Perl nos permite ter estruturas de dados complexas. Você agora pode escrever alguma coisa como isso, e, do nada, ter um array de três dimensões!
    for $x (1 .. 10) {
        for $y (1 .. 10) {
            for $z (1 .. 10) {
                $AoA[$x][$y][$z] =
                    $x ** $y + $z;
            }
        }
    }
[N.T.: O nome da variável AoA? vem do inglês ``Array of Arrays'', e foi mantido conforme o original. O mesmo acontecerá com variáveis chamadas AoH? (``Array of Hashes''), HoA? (``Hash of Arrays'') e HoH? (``Hash of Hashes'')] Por mais simples que isso possa parecer, existem detalhes escondidos muito mais elaborados do que vêem os olhos! Como você imprime isso? Por que você não pode simplesmente dizer print @AoA? Como você ordena isso? Como você pode passar isso para uma função, ou obter um desses de uma função? Isso é um objeto? Você pode salvá-lo no disco para obtê-lo de volta mais tarde? Como você acessa as linhas e colunas dessa matriz? Todos os valores precisam ser numéricos? Como você pode ver, é fácil ficar confuso. Embora uma pequena parte da dificuldade pudesse ser atribuída à implementação baseada em referências, era muito mais devido a auxência de documentação com exemplos escritos para o iniciante. Este documento tem como objetivo ser um tratamento detalhado porém compreensível de muitos tupos de estruturas de dados que você poderá precisar. Ele também serve como um ``livro de receitas'', ou de exemplos. Dessa forma, quando você precisar criar uma dessas estruturas complexas, você pode simplesmente selecionar um dos exemplos disponíveis aqui. Vamos ver cada uma dessas possíveis estruturas em detalhe. Temos seções separadas para cada um desses itens: Mas, por enquanto, vamos dar uma olhada em questões mais gerais, comuns a todos estes tipos de estruturas de dados.

voltar para o topo


REFERÊNCIAS

A coisa mais importante para compreender todas as estruturas de dados em Perl -- incluindo as arrays multidimensionais -- é que, embora possa não parecer, os @ARRAYs e %HASHes em Perl são sempre unidimensionais; eles só podem guardar valores escalares (strings, números ou referências). Eles não podem conter outras arrays ou hashes, mas, ao invés disso, podem conter referências para outros arrays e hashes. Você não pode usar uma referência para um array ou um hash exatamente da mesma maneira que você usaria um array ou hash diretamente. Para programadores de C ou C++ isso pode parecer confuso. Se for o caso, pense que existe uma diferença entre a estrutura e o ponteiro para a estrutura. (N.T.: É como aquele ditado zen que diz: ``O dedo aponta para a lua, mas ai daquele que confundir o dedo com a lua.'') Você pode (e deve) ler mais sobre referências em the perlref manpage. Basicamente, referências são como ponteiros, pois elas sabem para quem estão apontando. (Objetos também são um tipo de referência, mas nós não iremos precisar deles por enquanto.) Isso significa que quando você tem algo que parece conter uma estrutura de duas ou mais dimensões, o que você tem, na verdade, é simplesmente uma estrutura unidimensional contendo referências para o próximo nível. Na prática, é como se você pudesse usá-la como se fosse multidimensional. (É assim que a maioria das arrays multidimensionais em C também funcionam.)
    $array[7][12]                       # array of arrays
    $array[7]{string}                   # array of hashes
    $hash{string}[7]                    # hash of arrays
    $hash{string}{'another string'}     # hash of hashes
Agora, como o nível superior contém apenas referências, se você tentar imprimir sua array com uma função print(), você vai obter algo não muito bonito, como:
    @AoA = ( [2, 3], [4, 5, 7], [0] );
    print $AoA[1][2];
  7
    print @AoA;
  ARRAY(0x83c38)ARRAY(0x8b194)ARRAY(0x8b1d0)
Isso acontece porque Perl nunca dereferencia implicitamente suas variáveis. Se você quer a coisa para a qual a referência está apontando, então você precisa indicar isso explicitamente, com ``prefix typing indicators'' como ${$blah}, @{$blah}, @{$blah[$i]}, ou ``postfix pointer arrows'', como $a->[3], $h->{fred}, ou mesmo $ob->method()->[3].

voltar para o topo


ERROS COMUNS

Os dois erros mais comuns ao construir algo como uma array de arrays são a) acidentalmente contar o número de elementos ou b) obter uma referência para a mesmo alocação de memória repetidamente. Aqui está o exemplo em que você obtem uma contagem ao invés de uma lista aninhada:
    for $i (1..10) {
        @array = somefunc($i);
        $AoA[$i] = @array;      # ERRADO!
    }
Isto é simplesmente o mesmo que que atribuir um array a um escalar e obter sua contagem de elementos. Se fosse isso que você quisesse fazer, você poderia muito bem ser um pouquinho mais explícito, assim:
    for $i (1..10) {
        @array = somefunc($i);
        $counts[$i] = scalar @array;
    }
Aqui está o outro exemplo, de obter uma referência para a mesma alocação de memória, de novo e de novo:
    for $i (1..10) {
        @array = somefunc($i);
        $AoA[$i] = \@array;     # ERRADO!
    }
Mas, qual é o problema com isso? Parece certo, não parece? Afinal, eu acabei de dizer que você precisaria de um array de referências, então, por deus, você me deu uma! Infelizmente, embora isso seja verdade, ainda não funciona. Todas as referências em @AoA se referem a exatamente o mesmo lugar, e por isso irá conter o que quer que fique em @array no final. É similar ao problema demonstrado nesse programa em C:
    #include <pwd.h>
    main() {
        struct passwd *getpwnam(), *rp, *dp;
        rp = getpwnam("root");
        dp = getpwnam("daemon");
        printf("daemon name is %s\nroot name is %s\n",
                dp->pw_name, rp->pw_name);
    }
Que irá imprimir
    daemon name is daemon
    root name is daemon
O problema é que ambos rp e dp são ponteiros para a mesma locação da memória! Em C, você teria que lembrar de malloc() alguma memória nova. Em Perl, você usa o construtor de array [] ou o construtor de hash {}. Esta é a maneira correta de se fazer o código anterior funcionar:
    for $i (1..10) {
        @array = somefunc($i);
        $AoA[$i] = [ @array ];
    }
Os colchetes fazem uma referência para uma nova array com uma cópia do que está em @array no momento da atribuição. É isso o que você quer. [N.T.: O trecho a seguir está confuso.] Note que isto irá produzir um resultado similar, embora seja bem mais difícil de ler:
    for $i (1..10) {
        @array = 0 .. $i;
        @{$AoA[$i]} = @array;
    }
É a mesma coisa? Talvez sim, talvez não. A diferença sutil é que quando você atribui alguma coisa em colchetes, você tem certeza de que é uma nova referência com uma cópia dos dados. Algo completamente diferente pode acontecer com a dereferência do lado esquerdo da atribuição (@{$AoA[$i]}}). Tudo dependeria se $AoA[$i] estivesse indefinido, para começar, ou se já contivesse uma referência. Se @AoA já estivesse populada, como em
    $AoA[3] = \@another_array;
Nesse caso a atribuição com a dereferência do lado esquerdo usaria a referência que já estava lá:
    @{$AoA[3]} = @array;
É claro que isso teria o efeito ``interessante'' de sobrescrever (``clobbering'') @another_array. (Você já reparou que quando um programador diz que alguma coisa é ``interessante'', ao invés de significar ``intrigante'', é muito mais provável que signifique ``perturbador'', ``difícil'', ou os dois? :-)) Então, lembre-se apenas de sempre usar os construtores de array e de hash com [] ou {}, e você estará bem, mesmo que não seja optimamente eficiente. Surpreendentemente, a seguinte construção de aparência perigosa vai funcionar muito bem:
    for $i (1..10) {
        my @array = somefunc($i);
        $AoA[$i] = \@array;
    }
Isso acontece por que my() é mais uma declaração de tempo de execução do que de tempo de compilação por si. Isso significa que a variável em my() é renovada constantemente, a cada volta do loop. Assim, embora parecesse que você estava guardando a mesma variável cada vez, na verdade não era isso que você estava fazendo! Esta distinção sutil pode produzir código mais eficiente, sob o risco de enganar todos exceto os programadores mais experientes. Por isso, eu normalmente recomendo não ensinar isso para iniciantes. De fato, a não ser quando vou passar argumentos para uma função, eu nunca gosto de ver o operador ``me-dá-uma-referência'' (a contrabarra) por todo o código. Ao invés disso, eu recomendo aos iniciantes que eles (e a maioria de nós) tentem usar os construtores muito mais legíveis [] e {}, ao invés de esperar que o escopo léxico (ou dinâmico) e a contagem de referências façam a coisa certa por trás das cenas. Em resumo:
    $AoA[$i] = [ @array ];      # usually best
    $AoA[$i] = \@array;         # perilous; just how my() was that array?
    @{ $AoA[$i] } = @array;     # way too tricky for most programmers

voltar para o topo


PROBLEMA DE PRECEDÊNCIA

Por falar em coisas como @{$AoA[$i]}, os dois exemplos a seguir são, na verdade, a mesma coisa:
    $aref->[2][2]       # claro
    $$aref[2][2]        # confuso
That's because Perl's precedence rules on its five prefix dereferencers (which look like someone swearing: $ @ * % &) make them bind more tightly than the postfix subscripting brackets or braces! This will no doubt come as a great shock to the C or C++ programmer, who is quite accustomed to using *a[i] to mean what's pointed to by the i'th element of a. That is, they first take the subscript, and only then dereference the thing at that subscript. That's fine in C, but this isn't C. The seemingly equivalent construct in Perl, $$aref[$i] first does the deref of $aref, making it take $aref as a reference to an array, and then dereference that, and finally tell you the i'th value of the array pointed to by $AoA. If you wanted the C notion, you'd have to write ${$AoA[$i]} to force the $AoA[$i] to get evaluated first before the leading $ dereferencer.

voltar para o topo


POR QUE VOCÊ DEVE SEMPRE USAR use strict

Se tudo isso está começando a parecer mais assustador do que vale a pena, relaxe. Perl tem algumas características para ajudá-lo a evitar os erros mais comuns. A melhor maneira de evitar confusão é iniciar todo programa assim:
    #!/usr/bin/perl -w
    use strict;
Desse modo, você será forçado a declarar todas as suas variáveis com my() e também impedirá a ``dereferência simbólica'' acidental. Portanto, se você tivesse feito isso:
    my $aref = [
        [ "fred", "barney", "pebbles", "bambam", "dino", ],
        [ "homer", "bart", "marge", "maggie", ],
        [ "george", "jane", "elroy", "judy", ],
    ];
    print $aref[2][2];
O compilador avisaria o erro imediatamente, em tempo de compilação, porque você estaria acessando acidentalmente @aref, uma variável não declarada, e você seria então lembrado para escrever da seguinte forma:
    print $aref->[2][2]

voltar para o topo


DEBUGANDO

Antes da versão 5.002, o debugger padrão de Perl não fazia um bom trabalho ao imprimir estruturas de dados complexas. Com 5.002 e acima, o debugger inclui várias funcionalidades novas, incluindo edição de linha de comando bem como o comando x para imprimir estruturas de dados complexas. Por exemplo, dada a atribuição para $AoA acima, aqui está a saída do debugger:
    DB<1> x $AoA
    $AoA = ARRAY(0x13b5a0)
       0  ARRAY(0x1f0a24)
          0  'fred'
          1  'barney'
          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'

voltar para o topo


EXEMPLOS DE CÓDIGO

Apresentados com poucos comentários (estes receberão suas próprias páginas de documentação algum dia), aqui estão pequenos exemplos de código que ilustram vários tipos de estruturas de dados.

voltar para o topo


ARRAYS DE ARRAYS

Declaração de uma ARRAY DE ARRAYS

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

Geração de uma ARRAY DE ARRAYS

 # reading from file
 while ( <> ) {
     push @AoA, [ split ];
 }
 # calling a function
 for $i ( 1 .. 10 ) {
     $AoA[$i] = [ somefunc($i) ];
 }
 # using temp vars
 for $i ( 1 .. 10 ) {
     @tmp = somefunc($i);
     $AoA[$i] = [ @tmp ];
 }
 # add to an existing row
 push @{ $AoA[0] }, "wilma", "betty";

Acesso e Impressão de uma ARRAY DE ARRAYS

 # one element
 $AoA[0][0] = "Fred";
 # another element
 $AoA[1][1] =~ s/(\w)/\u$1/;
 # print the whole thing with refs
 for $aref ( @AoA ) {
     print "\t [ @$aref ],\n";
 }
 # print the whole thing with indices
 for $i ( 0 .. $#AoA ) {
     print "\t [ @{$AoA[$i]} ],\n";
 }
 # print the whole thing one at a time
 for $i ( 0 .. $#AoA ) {
     for $j ( 0 .. $#{ $AoA[$i] } ) {
         print "elt $i $j is $AoA[$i][$j]\n";
     }
 }

voltar para o topo


HASHES DE ARRAYS

Declaração de um HASH DE ARRAYS

 %HoA = (
        flintstones        => [ "fred", "barney" ],
        jetsons            => [ "george", "jane", "elroy" ],
        simpsons           => [ "homer", "marge", "bart" ],
      );

Geração de um HASH DE ARRAYS

 # reading from file
 # flintstones: fred barney wilma dino
 while ( <> ) {
     next unless s/^(.*?):\s*//;
     $HoA{$1} = [ split ];
 }
 # reading from file; more temps
 # flintstones: fred barney wilma dino
 while ( $line = <> ) {
     ($who, $rest) = split /:\s*/, $line, 2;
     @fields = split ' ', $rest;
     $HoA{$who} = [ @fields ];
 }
 # calling a function that returns a list
 for $group ( "simpsons", "jetsons", "flintstones" ) {
     $HoA{$group} = [ get_family($group) ];
 }
 # likewise, but using temps
 for $group ( "simpsons", "jetsons", "flintstones" ) {
     @members = get_family($group);
     $HoA{$group} = [ @members ];
 }
 # adiciona novos membros na família
 push @{ $HoA{"flintstones"} }, "wilma", "betty";

Acesso e Impressão de um HASH DE ARRAYS

 # um elemento
 $HoA{flintstones}[0] = "Fred";
 # outro elemento
 $HoA{simpsons}[1] =~ s/(\w)/\u$1/;
 # print the whole thing
 foreach $family ( keys %HoA ) {
     print "$family: @{ $HoA{$family} }\n"
 }
 # print the whole thing with indices
 foreach $family ( keys %HoA ) {
     print "family: ";
     foreach $i ( 0 .. $#{ $HoA{$family} } ) {
         print " $i = $HoA{$family}[$i]";
     }
     print "\n";
 }
 # print the whole thing sorted by number of members
 foreach $family ( sort { @{$HoA{$b}} <=> @{$HoA{$a}} } keys %HoA ) {
     print "$family: @{ $HoA{$family} }\n"
 }
 # print the whole thing sorted by number of members and name
 foreach $family ( sort {
                            @{$HoA{$b}} <=> @{$HoA{$a}}
                                        ||
                                    $a cmp $b
            } keys %HoA )
 {
     print "$family: ", join(", ", sort @{ $HoA{$family} }), "\n";
 }

voltar para o topo


ARRAYS DE HASHES

Declaração de um ARRAY DE HASHES

 @AoH = (
        {
            Lead     => "fred",
            Friend   => "barney",
        },
        {
            Lead     => "george",
            Wife     => "jane",
            Son      => "elroy",
        },
        {
            Lead     => "homer",
            Wife     => "marge",
            Son      => "bart",
        }
  );

Geração de um ARRAY DE HASHES

 # lendo de um arquivo
 # formato: LEAD=fred FRIEND=barney
 while ( <> ) {
     $rec = {};
     for $field ( split ) {
         ($key, $value) = split /=/, $field;
         $rec->{$key} = $value;
     }
     push @AoH, $rec;
 }
 # lendo de um arquivo
 # formato: LEAD=fred FRIEND=barney
 # sem temp
 while ( <> ) {
     push @AoH, { split /[\s+=]/ };
 }
 # chamando uma função que retorna uma lista de pares chave/valor como
 # "lead","fred","daughter","pebbles"
 while ( %fields = getnextpairset() ) {
     push @AoH, { %fields };
 }
 # likewise, but using no temp vars
 while (<>) {
     push @AoH, { parsepairs($_) };
 }
 # adiciona chave/valor a um elemento
 $AoH[0]{pet} = "dino";
 $AoH[2]{pet} = "santa's little helper";

Acesso e Impressão de um ARRAY DE HASHES

 # um elemento
 $AoH[0]{lead} = "fred";
 # outro elemento
 $AoH[1]{lead} =~ s/(\w)/\u$1/;
 # imprime tudo com refs
 for $href ( @AoH ) {
     print "{ ";
     for $role ( keys %$href ) {
         print "$role=$href->{$role} ";
     }
     print "}\n";
 }
 # imprime tudo com índices
 for $i ( 0 .. $#AoH ) {
     print "$i is { ";
     for $role ( keys %{ $AoH[$i] } ) {
         print "$role=$AoH[$i]{$role} ";
     }
     print "}\n";
 }
 # imprime tudo, um por vez
 for $i ( 0 .. $#AoH ) {
     for $role ( keys %{ $AoH[$i] } ) {
         print "elt $i $role is $AoH[$i]{$role}\n";
     }
 }

voltar para o topo


HASHES DE HASHES

Declaração de um HASH DE HASHES

 %HoH = (
        flintstones => {
                lead      => "fred",
                pal       => "barney",
        },
        jetsons     => {
                lead      => "george",
                wife      => "jane",
                "his boy" => "elroy",
        },
        simpsons    => {
                lead      => "homer",
                wife      => "marge",
                kid       => "bart",
        },
 );

Geração de um HASH DE HASHES

 # lendo de um arquivo
 # flintstones: lead=fred pal=barney wife=wilma pet=dino
 while ( <> ) {
     next unless s/^(.*?):\s*//;
     $who = $1;
     for $field ( split ) {
         ($key, $value) = split /=/, $field;
         $HoH{$who}{$key} = $value;
     }
 # lendo de um arquivo; mais temps
 while ( <> ) {
     next unless s/^(.*?):\s*//;
     $who = $1;
     $rec = {};
     $HoH{$who} = $rec;
     for $field ( split ) {
         ($key, $value) = split /=/, $field;
         $rec->{$key} = $value;
     }
 }
 # chamando uma função que retorna um hash chave,valor
 for $group ( "simpsons", "jetsons", "flintstones" ) {
     $HoH{$group} = { get_family($group) };
 }
 # likewise, but using temps
 for $group ( "simpsons", "jetsons", "flintstones" ) {
     %members = get_family($group);
     $HoH{$group} = { %members };
 }
 # adiciona novos mebros a família existente
 %new_folks = (
     wife => "wilma",
     pet  => "dino",
 );
 for $what (keys %new_folks) {
     $HoH{flintstones}{$what} = $new_folks{$what};
 }

Acesso e Impressão de um HASH DE HASHES

 # um elemento
 $HoH{flintstones}{wife} = "wilma";
 # outro elemento
 $HoH{simpsons}{lead} =~ s/(\w)/\u$1/;
 # imprime tudo
 foreach $family ( keys %HoH ) {
     print "$family: { ";
     for $role ( keys %{ $HoH{$family} } ) {
         print "$role=$HoH{$family}{$role} ";
     }
     print "}\n";
 }
 # imprime tudo reordenado
 foreach $family ( sort keys %HoH ) {
     print "$family: { ";
     for $role ( sort keys %{ $HoH{$family} } ) {
         print "$role=$HoH{$family}{$role} ";
     }
     print "}\n";
 }
 # imprime tudo reordenado pelo número de membros
 foreach $family ( sort { keys %{$HoH{$b}} <=> keys %{$HoH{$a}} } keys %HoH ) {
     print "$family: { ";
     for $role ( sort keys %{ $HoH{$family} } ) {
         print "$role=$HoH{$family}{$role} ";
     }
     print "}\n";
 }
 # establish a sort order (rank) for each role
 $i = 0;
 for ( qw(lead wife son daughter pal pet) ) { $rank{$_} = ++$i }
 # now print the whole thing sorted by number of members
 foreach $family ( sort { keys %{ $HoH{$b} } <=> keys %{ $HoH{$a} } } keys %HoH ) {
     print "$family: { ";
     # and print these according to rank order
     for $role ( sort { $rank{$a} <=> $rank{$b} }  keys %{ $HoH{$family} } ) {
         print "$role=$HoH{$family}{$role} ";
     }
     print "}\n";
 }

voltar para o topo


ESTRUTURAS MAIS ELABORADAS

Declaração de ESTRUTURAS MAIS ELABORADAS

Aqui está um exemplo mostrando como criar e usar um registro (``record'') cujos campos são de muitos tipos diferentes:
     $rec = {
         TEXT      => $string,
         SEQUENCE  => [ @old_values ],
         LOOKUP    => { %some_table },
         THATCODE  => \&some_function,
         THISCODE  => sub { $_[0] ** $_[1] },
         HANDLE    => \*STDOUT,
     };
     print $rec->{TEXT};
     print $rec->{SEQUENCE}[0];
     $last = pop @ { $rec->{SEQUENCE} };
     print $rec->{LOOKUP}{"key"};
     ($first_k, $first_v) = each %{ $rec->{LOOKUP} };
     $answer = $rec->{THATCODE}->($arg);
     $answer = $rec->{THISCODE}->($arg1, $arg2);
     # careful of extra block braces on fh ref
     print { $rec->{HANDLE} } "a string\n";
     use FileHandle;
     $rec->{HANDLE}->autoflush(1);
     $rec->{HANDLE}->print(" a string\n");

Declaração de um HASH DE ESTRUTURAS COMPLEXAS

     %TV = (
        flintstones => {
            series   => "flintstones",
            nights   => [ qw(monday thursday friday) ],
            members  => [
                { name => "fred",    role => "lead", age  => 36, },
                { name => "wilma",   role => "wife", age  => 31, },
                { name => "pebbles", role => "kid",  age  =>  4, },
            ],
        },
        jetsons     => {
            series   => "jetsons",
            nights   => [ qw(wednesday saturday) ],
            members  => [
                { name => "george",  role => "lead", age  => 41, },
                { name => "jane",    role => "wife", age  => 39, },
                { name => "elroy",   role => "kid",  age  =>  9, },
            ],
         },
        simpsons    => {
            series   => "simpsons",
            nights   => [ qw(monday) ],
            members  => [
                { name => "homer", role => "lead", age  => 34, },
                { name => "marge", role => "wife", age => 37, },
                { name => "bart",  role => "kid",  age  =>  11, },
            ],
         },
      );

Geração de um HASH DE ESTRUTURAS COMPLEXAS

     # Lendo de um arquivo
     # this is most easily done by having the file itself be
     # in the raw data format as shown above.  perl is happy
     # to parse complex data structures if declared as data, so
     # sometimes it's easiest to do that
     # here's a piece by piece build up
     $rec = {};
     $rec->{series} = "flintstones";
     $rec->{nights} = [ find_days() ];
     @members = ();
     # assume this file in field=value syntax
     while (<>) {
         %fields = split /[\s=]+/;
         push @members, { %fields };
     }
     $rec->{members} = [ @members ];
     # now remember the whole thing
     $TV{ $rec->{series} } = $rec;
     ###########################################################
     # Agora, você provaelmente quer fazer campos extras interessantes que
     # incluem ponteiros de volta na mesma estrutura de dados, então se
     # mudar uma peça, muda em todo local, como por exemplo
     # se você quis um campo {kids} que fosse uma referência
     # para um array de registros kids sem ter que duplicar
     # registros e sem ter problemas de atualização
     ###########################################################
     foreach $family (keys %TV) {
         $rec = $TV{$family}; # temp pointer
         @kids = ();
         for $person ( @{ $rec->{members} } ) {
             if ($person->{role} =~ /kid|son|daughter/) {
                 push @kids, $person;
             }
         }
         # REMEMBER: $rec and $TV{$family} point to same data!!
         $rec->{kids} = [ @kids ];
     }
     # Você copiou o array, mas o array em si contém ponteiros
     # para objetos não copiados. Isso significa que se você fazer Bart envelhecer via:
     $TV{simpsons}{kids}[0]{age}++;
     # Então isso também mudaria em
     print $TV{simpsons}{members}[2]{age};
     # pois $TV{simpsons}{kids}[0] e $TV{simpsons}{members}[2]
     # ambos apontam para a mesma tabela hash anônima
     # Imprime tudo
     foreach $family ( keys %TV ) {
         print "the $family";
         print " is on during @{ $TV{$family}{nights} }\n";
         print "its members are:\n";
         for $who ( @{ $TV{$family}{members} } ) {
             print " $who->{name} ($who->{role}), age $who->{age}\n";
         }
         print "it turns out that $TV{$family}{lead} has ";
         print scalar ( @{ $TV{$family}{kids} } ), " kids named ";
         print join (", ", map { $_->{name} } @{ $TV{$family}{kids} } );
         print "\n";
     }

voltar para o topo


Ligação de Banco de Dados

Você não pode facilmente amarrar uma estrutura de dados multinível (como hashes de hashes) para um arquivo dbm. O primeiro problema é que todos menos GBDM e o Berkeley DB tem limitações de tamanho, mas além disso, você também tem problemas em como referências vão se representadas no disco. Um módulo experimental que faz uma tentativa parcial de resolver esse necessidade é o MLDBM. Cheque o site CPAN mais próximo como descrito em the perlmodlib manpage para o código fonte do MLDBM.

voltar para o topo


VEJA TAMBÉM

the perlref manpage, the perllol manpage, the perldata manpage, the perlobj manpage

voltar para o topo


AUTOR

Tom Christiansen <tchrist@perl.com> Last update: Wed Oct 23 04:57:50 MET DST 1996

voltar para o topo


TRADUÇÃO

MarcoLima; Nicholas Amorim <nicholasamorim@gmail.com> Nelson Ferraz <nferraz@gmail.com>

voltar para o topo