Revista Do Linux  
EDIÇÃO DO MÊS
 WAP
 Perl
 Corporativo
 Entrevista
 CD do mês
 Capa
 Bluefish
 Estudo de Caso
 Linux World 2000
 Framemaker
 Produto
 LDP
 Mala Direta
 Windowmaker
 

Estruturas complexas em Perl

Dando prosseguimento ao artigo "Introdução ao Perl", na revista No 07, iremos nos aprofundar um pouco mais no uso do Perl. Ao longo dos próximos artigos iremos escrever uma agenda de contatos simples, utilizando vários recursos do Perl. O objetivo desse artigo será entender como funcionam estruturas de dados complexos no Perl.

Nessa agenda nós desejamos guardar os seguintes dados:

Nome da pessoa, Endereço, Telefone, Aniversário, Ocupação, Outras Notas.

Precisamos agora definir como armazenar esses dados. As variáveis do tipo scalar não nos interessam. Uma variável desse tipo pode armazenar apenas um dado de cada vez. Para os dados que queremos, precisaríamos ter para uma pessoa: $nome, $endereco, $telefone, $aniversario, $ocupacao, $outras

O primeiro problema que surge é como armazenar dados de diversos contatos juntos. Poderíamos ter variáveis scalares como $joao_endereco, $maria_aniversario e assim por diante. Mas isso não resolve o problema, pois precisaríamos saber, na hora de escrever o programa, o nome de todas as pessoas que estariam nessa agenda.

%HASHES - A variável hash é um tipo especial de variável encontrada em poucas linguagens. Uma hash associa vários nomes a um valor (scalar) cada. Podemos considerar que cada variável %hash é um minibanco de dados. Com a hash podemos ter uma estrutura um pouco melhorada dos dados: %endereco, %telefone, %niversario, %ocupacao, %outras

E podemos adicionar um contato da seguinte forma:



$endereco{`Carlos'}    = "R. 13 de Maio, 1450";
$telefone{`Carlos'}    = "555-5555";
$aniversario{`Carlos'} = "28/02";
$ocupacao{`Carlos'}    = "Dentista";
$outras{`Carlos'}      = "Primo do Mario";

Note que excluímos o nome, pois estamos usando-o como a "chave" dos hashes. Note também que estamos usando o simbolo `$' nas variáveis. Isso indica que estamos trabalhando com os elementos das hashes. A variável $endereco{`Carlos'} é um elemento de %endereço.

Existe outro motivo para se usar o `$'. Cada elemento de um hash é um scalar. Dessa forma, pode ser armazenado strings, números e referências, da mesma forma que se faz com uma variável scalar. No entanto, os nomes das variáveis são independentes. Se tivermos uma variável chamada $endereço ela será diferente de $endereço{Carlos}. A última, apesar de ser tratada como scalar, se refere ao elemento `Carlos' do hash %endereço.

Note que a chave de cada item é uma string (não confunda com $scalar). O uso dos `' é opcional, caso a string não contenha espaços ou caracteres especiais. Assim, podemos ter:



$endereco{Carlos}, $endereco{`João Cunha'}, $endereco{$nome}.

No último caso, assume-se que $nome contém a string desejada. O uso de hashes resolve pequenos problemas como esses.

Imagine que o Carlos tenha um celular e eu queira o número do celular na agenda.

Um novo hash %telefone_celular resolveria, mas imagine que o Marcos tenha 1 celular, 1 telefone residencial, 2 telefones no trabalho e mais 1 para fax. Nesse caso o simples uso do hash já não fica tão fácil.

Referências

Um scalar pode armazenar uma referência para uma variável. Uma referência é a posição interna no programa em Perl, onde uma variável se encontra. Dessa forma, uma referência "aponta" para uma variável. Assim:



$a = "oi";
$b = \$a;

$b contém a posição interna da variável $a. A barra `\' indica que queremos a referência da variável $a e não o seu valor. Se tentarmos ver o resultado: print $b; teremos algo como: SCALAR(0x80e2eec), que somente interessa ou faz sentido para o interpretador Perl. Referências valem para qualquer tipo de variável, assim:



%idade = (
      Carlos => 26,
      Amanda => 21
      );
@nomes = (`Carlos', `João', `Amanda');
$a = \%idade;
$b = \@nomes;
$c = \$idade{Carlos};
$d = \$nomes[1];

Note que os parênteses foram usados para declarar os valores de %idade e @nomes. No caso da hash %idade isso é possível porque um hash é visto internamente pelo Perl como uma array, com a diferença de que cada elemento tem uma "chave" (ou nome) associado a ele. Por isso, é comum encontrar o nome "Associative Array" se referindo ao hash.

As variáveis $a e $b contêm respectivamente referências para um hash e um array e as variáveis $c e $d para dois elementos do hash e array, ou seja, referências para variáveis scalares.

As referências podem ser usadas no lugar das variáveis originais às quais elas se referem. Para isso usamos uma técnica chamada de desreferenciamento. Para isso basta especificar o tipo de variável que estamos desreferenciando com o seu símbolo na frente.

Assim, %$a pode ser usado da mesma maneira que se usuaria %idade. $$a{Amanda} se refere ao elemento `Amanda' de %idade. $$c se refere ao elemento `Carlos'. No caso de $c, não é possível desreferenciá-lo com % como em %$a, porque o tipo de referência que ele possui não é para um hash, mas sim para um scalar (mesmo que esse scalar seja um elemento de um hash).

Por outro lado, o Perl nos permite associar elementos de uma array ou hash diretamente pela referência, sem precisar desreferenciá-la. Nesse caso:

$a->{Carlos} é a mesma coisa que $idade{Carlos} ou $$c

e

$b->[1] é a mesma coisa que $nomes[1]ou $$d

Voltando à agenda de contatos, podemos ter uma idéia de como resolver a questão de vários telefones para um mesmo contato. Podemos usar referências de hash. Considerando que o Carlos tenha 3 telefones, podemos colocar todos os telefones em um lista, ou array, e guardar a sua referência:




@telefones_do_Carlos=(`55-5555',`911-3456',`33-2323');
$telefone{Carlos} = \@telefones_do_Carlos;


Agora os elementos de %telefone são na verdade referências para @arrays, que contém os dados. Para obtermos o segundo telefone da lista (o primeiro tem o índice 0), podemos fazer algo como:



print ${$telefone{Carlos}}[1];

Note que os primeiros `{}' servem para especificarmos que estamos nos referindo a $telefone{Carlos} e não apenas $telefone. Se tivermos:



$ref = \%telefone;

O mesmo elemento poderia ser acessado:



${$$ref{Carlos}}[1];
${$ref->{Carlos}}[1];
$$ref{Carlos}->[1];

ou para simplificar:



$ref->{Carlos}->[1];

pode-se ler isso como: o segundo elemento da array referenciada pelo elemento `Carlos' da hash referênciada por $ref. Você deve ter notado que essa forma de trabalhar, apesar de mais eficiente, nos traz de volta ao problema original.

Precisaríamos criar várias arrays como @telefones_do_Carlos para podermos referenciá-las em %telefone. Note que se @telefones_do_Carlos for alterado, as referências em $telefone{Carlos} também serão.

Variáveis Anônimas

Isso nos leva a um conceito muito interessante no Perl, chamado de variáveis anônimas. Como o nome indica, uma variável anônima é uma variável que não possui nome definido.Para isso, ela já é criada na forma de referência.



$array_ref = [ 1, 3, `oi', 45 ];
$hash_ref  = {
          Laranja => '12,50',
          Maçã    => `7,60'
         };
$scalar_ref = \"Uma string";

Ou seja, os `[]' criam uma array e passam sua referência para $array_ref, os `{}' fazem a mesma coisa com um hash. No caso do scalar, não existe um símbolo especial como esses, então pegamos a referência diretamente da string com o `\'. É importante notar que a única forma de existência de uma variável anônima se dá através de sua referência. Se todas as suas referências forem eliminadas (associando um outro valor a $array_ref, por exemplo), a variável anônima é destruída e apagada da memória.

Podemos reescrever o hash de telefone da seguinte forma:



$telefone{Carlos}=[`55-5555',`911-3456',`33-2323'];

Assim, $telefone{Carlos} contém uma referência para uma array (que contém os telefones).Mas se é possível facilitar a escrita dos telefones, também podemos facilitar para todos os dados do contato:



$contato = {
      nome        => `Carlos',
      endereco    => ` R. 13 de Maio, 145',
      aniversario => "28/02",
      ocupacao    => "Dentista",
      outras      => "Primo do Mario",
      telefone    => [`55-5555',`911-3456',`33-2323']
      }

Dessa forma, sempre que quisermos nos referir ao Carlos só será necessária a variável $contato. Vamos expandindo a capacidade de dados armazenados na nossa agenda:



$contato = {
      `Carlos Rocha' => {
                endereco     => { 
                 Residencial => {
  endereco => `R. 13 de Maio, 145',
CEP      => `',
cidade   => `São Paulo'
                },
                Comercial   => {
                endereco => `Av. Silva Jardim, 5445',
                CEP      => '54.376-000,
                cidade   => `Florianópolis'                          }
                  },                                     aniversario => "28/02",
                 ocupacao => ["Dentista", "Estudante de     Administração"]
                   outras      => "Primo do Mario",
                   telefone    => {
                   `555-5555'  => `comercial',
                   `555-5513'  => `fax',
                `9114-3456' => `celular',
                `333-2323'  => `residencial'
                          }
                  }
       };

Mantivemos em letras minúsculas os campos não variáveis.

$contato é uma referência para um hash, onde cada "chave" é o nome da pessoa a que nos referimos.

Cada elemento (com o nome do contato) é uma referência para uma estrutura de dados desse contato.

Além do elemento `Carlos Rocha', podem haver mais contatos na mesma hash referenciada por contato. Em quase todos os elementos, usamos hashes para montar as estruturas. O motivo é que dessa forma podemos facilmente expandir as informações ilimitadamente.

Se o Carlos comprar uma casa na praia e quisermos colocá-la na agenda, basta colocar um novo elemento `Casa de Praia' em $contato->{`Carlos Rocha'}->{endereco}.

Ainda usamos uma array no campo `ocupacao' porque não nos interessa saber mais informações sobre cada ocupação.

Embora o exemplo que estamos usando é o de uma agenda simples de contato, as técnicas vistas aqui podem ser empregadas em estruturas muito mais complexas para diversos tipos de aplicações.

Para finalizar, vamos escrever uma pequena sub-rotina que mostra na tela os contatos.

No próximo artigo abordaremos como armazenar e recuperar esses dados em arquivos.


função.pl



sub mostra_contatos{
  #$contato é o parâmetro passado para a função
  my ($contato) = @_; 
  #Vamos predeclarar cada variável antes de usá-la
  my ($nome, $ocupacao, $tipo_endereco, $telefone);

 # O foreach executa um loop para cada item da array à direita 
  # no caso (keys(%$contato)), gravando o valor atual na variável 
  # $nome
  # Primeiro desreferenciamos $contato para ser tratado como um hash
  # e usamos a função keys() para gerar uma lista com todas as chaves
  # da hash %$contato
  foreach $nome (keys(%$contato)){
    print "Nome: $nome\n";
    print "Ocupação: \n";
#agora desreferenciamos a array anônima de ocupação
foreach $ocupacao (@{$contato->{$nome}->{ocupacao}}){
print "\t$ocupacao\n";
    }
print "Aniversário: " . $contato->{$nome}->{aniversario} . "\n";
    print "Endereços: \n";
    #Agora para a hash referenciada em endereco
    foreach $tipo_endereco (keys(%{$contato->{$nome}->{endereco}})){
     print "\t$tipo_endereço:\n";
     print "\t\tEndereço: " . $contato->{$nome}->{endereco}->{$tipo_endereco}->{endereco} . "\n";
     print "\t\tCEP     : " . $contato->{$nome}->{endereco}->{$tipo_endereco}->{cep} . "\n";
      print "\t\tCidade  : " . $contato->{$nome}->{endereco}->{$tipo_endereco}->{cidade} . "\n";
    }

    #Finalmente para os telefones
    print "Telefones: \n";
    foreach $telefone (keys(%{$contato->{$nome}->{telefone}})){
      print "\t" . $contato->{$nome}->{telefone}->{$telefone} . ": $telefone\n";    
    }

    print "Outras Informações: \n\t" . $contato->{$nome}->{outras} ."\n";
   }
#Agora para separar entre os contatos
  print "-"x40;
  }


A Revista do Linux é editada pela Conectiva S/A
Todos os Direitos Reservados.

Política de Privacidade
Anuncie na Revista do Linux