r11 - 14 Jul 2009 - RonaldoLima
- NOME
- DESCRIÇÃO
- REFERÊNCIAS
- ERROS COMUNS
- PROBLEMA DE PRECEDÊNCIA
- POR QUE VOCÊ DEVE SEMPRE USAR
use strict - DEPURAÇÃO
- EXEMPLOS DE CÓDIGO
- ARRAYS DE ARRAYS
- Declaração de uma ARRAY DE ARRAYS
- Geração de uma ARRAY DE ARRAYS
- Acesso e Impressão de uma ARRAY DE ARRAYS
- HASHES DE ARRAYS
- Declaração de um HASH DE ARRAYS
- Geração de um HASH DE ARRAYS
- Acesso e Impressão de um HASH DE ARRAYS
- ARRAYS DE HASHES
- Declaração de um ARRAY DE HASHES
- Geração de um ARRAY DE HASHES
- Acesso e Impressão de um ARRAY DE HASHES
- HASHES DE HASHES
- Declaração de um HASH DE HASHES
- Geração de um HASH DE HASHES
- Acesso e Impressão de um HASH DE HASHES
- ESTRUTURAS MAIS ELABORADAS
- Declaração de ESTRUTURAS MAIS ELABORADAS
- Declaração de um HASH DE ESTRUTURAS COMPLEXAS
- Geração de um HASH DE ESTRUTURAS COMPLEXAS
- Ligação de Banco de Dados
- VEJA TAMBÉM
- AUTOR
- TRADUZINDO
NOME
perldsc - Perl Data Structures Cookbook (estruturas de dados complexas)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 ausê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 tipos 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.
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].
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ê obtêm 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 ]; # usualmente melhor
$AoA[$i] = \@array; # perilous; exatamente como my() estava naquele array?
@{ $AoA[$i] } = @array; # modo bastante complicado para muitos programadores
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.
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]
DEPURAÇÃO
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 comandox 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'
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.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
# lendo de um arquivo
while ( <> ) {
push @AoA, [ split ];
}
# chamando uma função
for $i ( 1 .. 10 ) {
$AoA[$i] = [ somefunc($i) ];
}
# usando variáveis temporárias
for $i ( 1 .. 10 ) {
@tmp = somefunc($i);
$AoA[$i] = [ @tmp ];
}
# adição uma linha existente
push @{ $AoA[0] }, "wilma", "betty";
Acesso e Impressão de uma ARRAY DE ARRAYS
# um elemento
$AoA[0][0] = "Fred";
# outro elemento
$AoA[1][1] =~ s/(\w)/\u$1/;
# imprimir tudo com refs
for $aref ( @AoA ) {
print "\t [ @$aref ],\n";
}
# imprimir tudo com índices
for $i ( 0 .. $#AoA ) {
print "\t [ @{$AoA[$i]} ],\n";
}
# imprimir tudo de um em um
for $i ( 0 .. $#AoA ) {
for $j ( 0 .. $#{ $AoA[$i] } ) {
print "elt $i $j is $AoA[$i][$j]\n";
}
}
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
# lendo de um arquivo
# flintstones: fred barney wilma dino
while ( <> ) {
next unless s/^(.*?):\s*//;
$HoA{$1} = [ split ];
}
# lendo de um arquivo; mais variáveis temporárias
# flintstones: fred barney wilma dino
while ( $line = <> ) {
($who, $rest) = split /:\s*/, $line, 2;
@fields = split ' ', $rest;
$HoA{$who} = [ @fields ];
}
# chamando uma função que retorna uma lista
for $group ( "simpsons", "jetsons", "flintstones" ) {
$HoA{$group} = [ get_family($group) ];
}
# idem, mas usando variáveis temporárias
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/;
# imprimir todas as coisas
foreach $family ( keys %HoA ) {
print "$family: @{ $HoA{$family} }\n"
}
# imprimir na totalidade com índices
foreach $family ( keys %HoA ) {
print "family: ";
foreach $i ( 0 .. $#{ $HoA{$family} } ) {
print " $i = $HoA{$family}[$i]";
}
print "\n";
}
# imprimir na totalidade ordenado por número dos membros
foreach $family ( sort { @{$HoA{$b}} <=> @{$HoA{$a}} } keys %HoA ) {
print "$family: @{ $HoA{$family} }\n"
}
# imprimir na totalidade ordenado pelo número e nome dos membros
foreach $family ( sort {
@{$HoA{$b}} <=> @{$HoA{$a}}
||
$a cmp $b
} keys %HoA )
{
print "$family: ", join(", ", sort @{ $HoA{$family} }), "\n";
}
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 };
}
# idem, mas sem usar variáveis temporárias
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";
}
}
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) };
}
# idem, mas usando variáveis temporárias
for $group ( "simpsons", "jetsons", "flintstones" ) {
%members = get_family($group);
$HoH{$group} = { %members };
}
# adiciona novos membros 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";
}
# estabelece um critério de ordenação (rank) para cada parte
$i = 0;
for ( qw(lead wife son daughter pal pet) ) { $rank{$_} = ++$i }
# agora imprime tudo ordenado pelo números dos membros
foreach $family ( sort { keys %{ $HoH{$b} } <=> keys %{ $HoH{$a} } } keys %HoH ) {
print "$family: { ";
# e imprime estes de acordo com a ordem do rank
for $role ( sort { $rank{$a} <=> $rank{$b} } keys %{ $HoH{$family} } ) {
print "$role=$HoH{$family}{$role} ";
}
print "}\n";
}
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);
# cuidado de bloco extra de colchetes na referência a filehandle
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
# aqui está acumulado fragmento por fragmento
$rec = {};
$rec->{series} = "flintstones";
$rec->{nights} = [ find_days() ];
@members = ();
# assume este arquivo na sintaxe field=value
while (<>) {
%fields = split /[\s=]+/;
push @members, { %fields };
}
$rec->{members} = [ @members ];
# agora guarde de tudo
$TV{ $rec->{series} } = $rec;
###########################################################
# Agora, você provavelmente 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}; # ponteiro temporário
@kids = ();
for $person ( @{ $rec->{members} } ) {
if ($person->{role} =~ /kid|son|daughter/) {
push @kids, $person;
}
}
# LEMBRE-SE: $rec e $TV{$family} apontam para os mesmos dados!!
$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";
}

