Skip to content

hernan604/Programando--Perl--Moderno

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

42 Commits
 
 

Repository files navigation

PROGRAMANDO PERL MODERNO - PARA INICIANTES

AVISO

Este documento ainda está em desenvolvimento.

RESUMO

O público alvo deste tutorial é principalmente novos programadores da linguagem perl. A intenção é que este manual tenha qualidade suficiente para servir de referência e atrair mais e mais programadores ao fantástico universo da linguagem perl. A intenção inicial do autor é esclarecer dúvidas que o próprio autor gostaria que lhe tivessem sido esclarecidas o quanto antes.

A parte de "Temas Livres" ao final deste documento está aberta para quem quiser contribuir. Deixei alguns temas como exemplo.. a idéia colocar soluções para problemas do dia-a-dia que usuários podem enfrentar e servir como referência para consulta.

Entre em contato, ou submeta um pull request com o fix caso encontre erro neste documento.

Toda ajuda é bem vinda para melhorar este documento.

Instalação do perl

É imporante ressaltar que algumas máquinas linux, mac e bsd vem com um perl instalado por padrão. Esse perl é utilizado pela máquina para gerenciar N coisas e não é recomendável mexer nele pois sua máquina depende dele assim como suas aplicações. Para não gerar conflito de desinstalações e evitar quebras ou uso de root, recomanda-se a instalação de uma versão local, que fica dentro do diretório do seu usuário. Com essa configuração de perl local é possível instalar módulos sem a necessidade de usuário root. Isso tambem é importante pois separa bem quais usuários executam e tem acesso a quais partes do sistema.

Para verificar se tem perl na máquina, digite:

$ perl -v

perlbrew

Primeiro você verifica se tem alguma versão perl já instalada no sistema:

$ perl -v

Agora sim, instale o perlbrew:

$ curl -L http://install.perlbrew.pl | bash

$ source ~/perl5/perlbrew/etc/bashrc

$ perlbrew available

$ perlbrew -j 8 install 5.19.1        # use 8 threads para compilar

Essa linha que você executou:

$ source ~/perl5/perlbrew/etc/bashrc

precisa ser adicionada no ~/.bash_profile ou ~/.bashrc . Pois ela precisa ser executada sempre antes de usar a versão esperada do perl. É só adicionar no final desses arquivos e depois sair do terminal e abrir ele novamente. Executar o comando perl -v para ter certeza que o perlbrew está sendo executado corretamente e a versão apresentada é a correta que você instalou.

Após finalizar a instalação, altere a versão para a que você instalou:

$ perlbrew switch 5.19.1

e verifique a versão:

$ perl -v

Depois instale o cpanm. É com o cpanm que se instala módulos perl.

$ perlbrew install-cpanm

E para instalar módulos com o cpanm:

$ cpanm Nome::Modulo1 Modulo2::Etc

local::lib

O local::lib é uma alternativa ao perlbrew. Esta veio muito antes do perlbrew e é altamente testada, recomendada e utilizada.

curl -L http://cpanmin.us | perl - --local-lib=~/perl5 local::lib App::cpanminus && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib)

Feito isso, adicione a linha abaixo no seu ~/.bash_profile ou ~/.bashrc

eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)

E depois teste se a versão do perl está correta:

$ perl -v

Módulos perl

Uma caracteristíca da linguagem perl é o ecosistema que foi formado ao longo dos anos. Primeiro veio o CPAN onde foi possível compartilhar os módulos. Após isso foram sendo adicionadas mais e mais facilidades e assim foi formado o ecossistema perl que hoje contempla:

cpan            #repositório oficial
irc             #salas de bate papo para acesso direto aos desenvolvedores no mundo todo
documentação    #o cpan tem excelentes documentações junto aos módulos
testes          #muitas aplicações tem testes. Alem de servir como testes, serve como referência de uso das apps
request tracker #o request tracker RT do cpan é um produto usado pelas maiores empresas do mundo, incluindo a NASA

PAUSE

O PAUSE é nome do sistema onde são enviados os módulos. Todos os módulos que entram no CPAN passam pelo PAUSE.

http://pause.perl.org/

CPAN - O repositório oficial

O cpan é o repositório oficial no mundo perl. fica em www.cpan.org ou search.cpan.org. Lá você digita o termo e confere os módulos que solucionam essa tarefa.

Você pode digitar "google", "twitter","excel","json" para ter uma listagem de módulos relacionados à busca.

Existe um módulo chamado Task::Kensho que foi criado para servir de referência sobre quais os melhores módulos para determinadas tarefas. O módulo Task::Kensho serve como link para outros módulos que já estão 'reconhecidos' como sendo os mais indicados. O Task::Kensho pode ser encontrado em:

https://metacpan.org/pod/Task::Kensho

Veja alguns exemplos de modulos no cpan:

https://metacpan.org/pod/Mango
https://metacpan.org/pod/Mojolicious::Guides
https://metacpan.org/pod/release/ETHER/Catalyst-Manual-5.9007/lib/Catalyst/Manual/Tutorial.pod
https://metacpan.org/pod/release/YANICK/Dancer-1.3120/script/dancer
https://metacpan.org/pod/Net::Twitter
https://metacpan.org/pod/WWW::YouTube::Download
https://metacpan.org/pod/Spreadsheet::WriteExcel

Documentação

Documentação é levada bem a sério no cpan. Existe alguns padrões mínimos a serem seguidos, por exemplo, todos os módulos perl tem no mínimo os seguintes itens em sua documentação:

SYNOPSIS      #exemplo rápido de uso
DESCRIPTION   #descrição detalhada
AUTHOR        #informações para contactar o autor
LICENSE       #informações da licensa

Isso é bacana pois ao procurar um módulo, é certo que dá pra ter uma noção do seu uso já na 'synopse'.

Toda a documentação é feita no formato POD ( plain old documentation )

POD

POD = plain old documentation. é um formato de documentação que pode residir junto aos código fonte perl. E durante a instalação do módulo, é gerada a documentação com base no conteúdo POD nos arquivos do módulo.

Este documento é feito em POD. Existem plugins que lêem POD e transformam em HTML, man pages, etc, diferentes formatos.

Você pode conferir o POD original deste documento em:

https://raw2.github.com/hernan604/Programando--Perl--Moderno/master/README.pod

Se você notar, vai ver várias diretivas:

=head2 Título heading 2
=head3 Título heading 3

etc... são 'tags' do formato POD.

Código fonte

É possível baixar e visualizar o código fonte. O link de download indica um arquivo.tar.gz que é o fonte do módulo em si. No entanto tambem é possível visualizar os códigos fonte clicando em 'source', ex:

https://metacpan.org/source/MMIMS/Net-Twitter-4.01002/lib/Net/Twitter.pm

Testes

Os testes são levados bastante a sério no desenvolvimento de módulos perl que estão no cpan. São os testes bem feitos que vão garantir que determinado módulo continua funcionando ou quebrou.

A execução de-facto é realizada por pessoas e empresas com cluster de computadores que utilizam um módulo de "framework de smoke test" que agilizam o teste em massa de módulos enviados ao cpan. Isto que dizer que todo módulo que é enviado ao cpan é testado nas mais diversas plataformas (win/linux/bsd/etc) e nas mais diversas versões perl. Assim que um teste é executado, em caso de erro um LOG é enviado por email ao desenvolvedor. É um log igual ao que o desenvolvedor teria se estivesse sentado e executase a aplicação na máquina do teste. Então o LOG pode ser analisado e o problema pode ser corrigido. Feito isso é possível gerar uma nova release e subir novamente ao cpan, e verificar se o problema foi solucionado.

Então isso facilita muito pois você precisa criar os testes, executar e fazer eles passarem na sua máquina. Ao subir o módulo no cpan, os testers tem framework automatizado para testar sua app em outras plataformas, ai você recebe os logs por email em caso de erro, arruma e sobe uma nova versão. Se o problema for em uma versão distinta à que em instalada em sua máquina, você pode usar o perlbrew e instalar a versão que deu pau para tentar simular o erro localmente e encontrar soluções.

Tendo em vista que os framework de testes automatizam para todas as versões de perl e plataformas, isto quer dizer que ao sair uma nova versão do perl, seu módulo é testado automaticamente. E se tiver problemas, será enviado o LOG.

Tudo isso pode ser encontrado através de links dentro de um módulo no cpan e metacpan.

Veja exemplo de matriz de testes, é possível ver a versão perl e plataforma. E, na coluna esquerda é possível ver um resumo da evolução e versão dos módulos.

http://www.cpantesters.org/distro/N/Net-Twitter.html#Net-Twitter-4.01002?oncpan=1&distmat=1
http://www.cpantesters.org/distro/M/Mojolicious.html#Mojolicious-4.72?oncpan=1&distmat=1
http://www.cpantesters.org/distro/D/Dancer.html#Dancer-1.3120?oncpan=1&distmat=1
http://www.cpantesters.org/distro/S/Spreadsheet-ParseExcel-Simple.html#Spreadsheet-ParseExcel-Simple-1.04?oncpan=1&distmat=1

Existe até um ranking para saber quem testou mais módulos:

http://stats.cpantesters.org/testers.html

Além de um contador ao vivo:

http://stats.cpantesters.org/

Autores dos módulos

No cpan, junto aos módulos é possivel encontrar dados para entrar em contato com o responsável pelo módulo. Normalmente tem email e nome, github, etc.

Se um dia estiver desenvolvendo e precisar de uma customização, suporte ou melhoria em módulo, pode entrar em contato direto e contratar os serviçoes.

RT - Request Tracker

Todos os autores de módulo tem à disposição o RT, para registrar e solucionar bugs nos módulos.

RT: http://www.bestpractical.com/rt/who.html

IRC - chat com os desenvolvedores

Existem salas de batepapo em um servidor oficial perl. Para acessar, é necessário conectar no servidor IRC abaixo através dos apps mibbit, mIRC, xchat, irssi, pidgin, entre outros.

irc.perl.org

Os módulos mais usados tem salas de batepapo bastante ativas com umas 300 pessoas e são discutidas soluções. Tambem rola muita ajudas.

O ponto bacana é a facilidade do contato direto com os criadores dos mais diversos módulos/frameworks e até desenvolvedores da linguagem perl.

Metacpan

Metacpan é um cpan com cara mais nova. Ele foi criado para ser uma alternativa ao cpan oficial. O metacpan tambem é open source, no entanto este utiliza Elastic::Search para realizar as buscas instantâneas. Outro ponto bacana sobre o metacpan é a possibilidade de forkar o projeto e contribuir com plugins e perfumaria para o site.

Ajuda

As instalações perl inclúem por padrão o perldoc, programas que nos ajuda como referência e manual de uso. Através dele é possível visualizar os documentação/manuais de módulos e funções. É similar ao comando "man", e sempre é possível executar um "man Nome::Modulo"

Exemplo para ler os manuais de pragmas internos do perl:

$ perldoc perl     # índice
$ perldoc pragma
$ perldoc strict
$ perldoc warnings
$ perldoc perldoc  ou na fonte: https://metacpan.org/pod/release/MALLEN/Pod-Perldoc-3.21/perldoc.pod

Exemplo para ler manual de um módulo:

$ perldoc Some::Module

Exemplo para ler o manual das funções internas:

$ perldoc -f sort
$ perldoc -f qw
$ perldoc -f push
$ perldoc -f shift

Embora a leitura seja pesada, tem bastante coisa útil dentro desses manuais. Talvez não tanto para principiantes, mas é sempre bom se acostumar a usar o perldoc como referência na hora de usar um comando, ou, módulo. E aos poucos o seu uso torna-se natural.

Não menos importante, no irc.perl.org tambem é possível obter ajuda.

Execução de um script perl

Para executar o script perl , você vai ter que abrir um terminal e ir até o diretório onde se encontra seu script. Caso esteja numa máquina windows, o linux é bastante recomendável pois agiliza bem o desenvolvimento com perl.

O jeito mais direto é assim:

cd /home/usuario/script/
perl meu_script.pl

Isso quer dizer que ele vai apenas chamar seu script e executar.

Existem outras opções ótimas para desenvolvedores, que possibilitam usar versões especificas de módulos que o script esteja usando. Ou seja, supondo que 'meu_script.pl' vai usar o módulo Modulo::XYZ, no caso acima seria usada a versão que está instalada junto com minha versão de perl atual.

No entanto, na hora de executar é possível indicar o diretório onde esse módulo está, utilizando o argumento -I../diretorio/modulo/lib (i maiúscilo seguido do diretorio/lib ) assim:

cd /home/usuario/script
perl -I/Downloads/Modulo-XYZ/lib/ meu_script.pl

Isso fará o script usar o Modulo::XYZ do diretório indicado: /Downloads/Modulo-XYZ/lib/ ao invés do que está instalado junto com seu perl.

Isso é bastante útil na hora de debugar algum módulo externo. E tambem para usar módulos fazendo apenas o download e indicando o diterório do modulo/lib. Ou seja, não necessariamente precisaria instalar o módulo... só é necessário especificar o local do módulo em tempo de execução. Para a aplicação é transparente. Dessa maneira é possível criar um 'bundle' que contém todas as depêndencias necessárias para executar o módulo.

Outra alternativa ao -I é usar o lib.

$ perldoc lib

O lib recebe uma lista de diretórios onde tem módulos que precisam ser carregados. Portanto é possível fazer algo assim:

use lib ('../Modulo1/lib/','../Modulo2/lib/'); # use lib './lib';
..codigo..

Resumindo, é possível passar o diretório dos módulos no momento da execução e tambem é possível setar eles junto ao código fonte usando o módulo lib.

shebang

Shebang é a sequência de caracteres #!

O shebang do perl é normalmente:

#!/usr/bin/perl

ou

#!/usr/bin/env perl

A primeira opção irá executar provavelmente o perl do sistema. Já a segunda irá executar o perl de acordo com as variáveis de ambiente de usuário. Podendo não ser o perl do sistema.

#!/bin/perl-5.19.9/bin/perl -I/bin/perl-5.19.9/lib/5.19.9/ -I/bin/perl-5.19.9/lib/site_perl/5.19.9 -I/bin/stuff/lib/

Conforme o exemplo acima Tambem é possível indicar os diretórios que devem ser carregados ao usar uma shebang. As vezes isto é extremamente útil. É útil pra chamar um cronjob, ou um programa que chama um script perl. emfim, pode ser útil em algumas situações.

Variáveis de ambiente

As variáveis de ambiente podem ser acessadas através da variável $ENV{"PATH"}.

Variáveis de ambiente tambem servem muito bem para salvar usuários e senhas de acesso nos sistemas da rede.

Escopo de variável

Os escopos estão presos aos {} e às 'sub {}'. Ou seja , blocos de código e métodos. Veja os exemplos de uma linha

$ perl -e '{my $a = 10; warn $a; } warn $a '
#saida
10 at -e line 1.
Warning: something's wrong at -e line 1.


$ perl -e ' my $a = 20; { my $a = 10; warn $a; } warn $a '
#saida
10 at -e line 1.
20 at -e line 1.


$ perl -e ' my $a = 20; { my $a = 10; warn $a; } warn $a; sub { my $a = 30; warn $a; }->() ; warn $a;'
#saida
10 at -e line 1.
20 at -e line 1.
30 at -e line 1.
20 at -e line 1.


$ perl -e ' my $a = 20; { my $a = 10; warn $a; { my $a = 40; warn $a; } warn $a } warn $a '
#saida
10 at -e line 1.
40 at -e line 1.
10 at -e line 1.
20 at -e line 1.

Métodos

Em perl, métodos são chamados 'subrotina' ou 'sub'. perldoc -f sub Todos os métodos perl são reconhecidos através da definição de subrotina 'sub' ex:

sub set_nome {
    my ( $nome ) = @_;
}

Métodos com orientação a objeto recebem o $self como primeiro argumento. $self é a referência para o próprio objeto. É como o this no javascript

sub set_nome {
    my ( $self, $nome ) = @_;
}

Os argumentos sempre chegam na lista @_. O @_ começa com @ portanto é um array de argumentos. É uma variável especial que representa o array de argumentos recebido. Então é possível criar uma lista de variáveis equalizar com os valores recebidos, isto fará cada variável conter o valor que chegou, ex:

my @argumentos = ( 'argumento_1', 'argumento_2' );
my ( $__arg1, $__arg2 ) = @argumentos;   # equaliza o array da esquerda com o da direita
warn $__arg1;   #printa o valor referente ao array da direita no momento da atribuição
warn $__arg2;   #printa o valor referente ao array da direita no momento da atribuição

my $metodo     = sub { 
    my ( $arg1, $arg2 ) = @_; #equaliza o array de argumentos de entrada (@_) com o array da esquerda
    warn "ARG1: ". $arg1;   # printa o valor referente
    warn "ARG2: ". $arg2;   # printa o valor referente
};
$metodo->( @argumentos ); #passa array como argumento

Você não passa o $self, ele é passado automaticamente para você sempre que trabalha com orientação a objetos. Eu poderia chamar tanto o primeiro quanto o segundo método da segunte maneira:

$pessoa->set_nome( "Bla" )

e $nome teria valor de "Bla"

Se precisar passar mais argumentos é possível fazer:

sub set_coisas {
    my ( $self, $coisa1, $coisa2, $coisa3 ) = @_;
}

$pessoa->set_coisas( 'item1', "item2", 3 );
# $coisa1 = 'item1'
# $coisa2 = 'item2'
# $coisa3 = 3

Tambem é possível executar uma sub anônima:

sub { my ( $arg1, $arg2 ) = @_; .......  }->( "Argumento1", "Coisa2" )

ou, atribuir a subrotina em uma variável e executar ela depois, ex:

my $pular = sub { warn "pular" };
$pular->();

Passando métodos como argumentos:

my $callback_send_msg = sub {
    my ( $msg ) = @_;
    warn $msg
};

e uma chamada de um objeto '$item' co método faca_algo

$item->faca_algo( "arg1", "arg2", $callback_send_msg );

and then inside the 'faca_algo' method:

sub faca_algo {
    my ( $arg1, $arg2, $callback ) = @_;
    ...
    $callback->( "Código executado com sucesso." );
}

Para acessar valores individuais com $_[NUMERO]:

sub faz_algo {
    my $primeiro = $_[0];
    my $segundo = $_[1];

    warn $primeiro;
    warn $segundo;
}

faz_algo( "foo", "bar" );
#saida
# foo at t.pl line 5.
# bar at t.pl line 6.

Métodos anônimos

Tambem é possível trabalhar com métodos/funções anônimas em perl. É possível passar métodos ou a referência deles como callback.

Função anônima, posso simplesmente criar um método anônimo que recebe 2 argumentos e já é executado:

perl -e 'sub { my ( $arg1, $arg2 ) = @_;  warn $arg1.$arg2 }->( "argumento1", "argumento2" ); '

Tambem é possível criar essa closure anônima ( perldoc -f sub ) e receber a referência do método. podendo usar a variável como se fosse um método.

perl -e 'my $metodo = sub { my ( $arg1, $arg2 ) = @_;  warn $arg1.$arg2 };  warn "---"; $metodo->( "argumento1", "argumento2" ); '

Callbacks, tambem é possível. É só passar a referência do método como argumento de outro método. Trivial.

perl -e ' my $callback = sub { my ( $msg ) = @_; warn $msg  }  ; sub {  my ( $cb ) = @_; $cb->( "here callback" ) }->( $callback )  '

Variáveis

Por padrão todas as variáveis começam com $ e devem ser declaradas com 'my'. ex:

my $variavel    = "algo";
my $var2        = 1;
my $array       = [ 1,2,3,"quatro" ];
my $carro       = Carro->new();
my @list        = ("aa","bb",1,2,3);
my %hash        = (nome=>"jão",idade=>"dimenor");

Constantes

use constant IP => "127.0.0.1" ;
warn IP ;
# 127.0.0.1 at t.pl line 2.

use DDP;
use constant NAMES => qw|joe mary|;
warn scalar NAMES;
# 2 at t.pl line 6.

foreach my $name ( NAMES ) {
    warn $name;
}
#   joe at t.pl line 9.
#   mary at t.pl line 9.


use constant FORM => ( 
    nome        => 'José',
    sobrenome   =>  'da Silva'
);
my %form = FORM;
warn p %form;
#   {
#       nome        "José",
#       sobrenome   "da Silva"
#   }

Tipos de variáveis / Estrutura de dados:

my $var         = [1, 2, 3];                                # ARRAY
my $pessoa      = { nome => 'jão', idade => 'dimenor' };    # HASH
my $var         = Some::Module->new();                      # referencia a objeto
my @list        = ("nome","jão");                           # lista, c/ 2 itens
my %hash        = @list;                                    # lista em contexto associativo, ex: nome => jão

scalar

$ perldoc perldata

Variáveis scalares podem ser números, strings ou referências para outras variáveis. A variável escalar é sempre uma única coisa e pode ser passada como argumento como uma única coisa.

Uma variável scalar sempre começa com $, ex:

$texto  = ""
$pessoa = { nome => 'Joe', idade => 20 };
$grupo  = [ $pessoa1, $pessoa2, $pessoa3 ];
$app    = Minha::App->new();
$numero = 20;

Repare que a variável scalar faz referência a apenas 1 coisa, seja um array, um hash, string, numero, instancia, etc, mas é sempre referência a um único valor. Já as listas ( variáveis que começam com @ ) são listas de itens scalares ou seja, uma lista pode conter vários scalares.

array e listas

Arrays e listas são muito parecidos e podem funcionar em conjunto, mas não são exatamente a mesma coisa.

array scalar (scalar é o $ cifrão ou sigíl): # e scalar é apenas uma coisa. neste caso é uma referência para um objeto do tipo ARRAY

$meu_array = [
    "coisa1",
    "coisa2"
]

lista (lista são os itens com os parentesis. O @ arroba é um array):

@minha_lista = (
    "coisa1",
    "coisa2"
);

resumindo:

@minha_lista                          <- array dereferenciado
$meu_array                            <- array referenciado
( "primeiro", "segundo", $terceiro )  <- lista com 3 itens

Dica, para não ter que ficar digitando aspas duplas toda hora, existe uma opção chamada "qw" que coloca os as aspas automaticamente. Mas só funciona para strings, ou números sem 'traço' ou espaços. ex:

@lista = (  qw|item1 item2 item3|  );    
#é a mesma coisa que 
@lista = ( 'item1', 'item2', 'item3' );

# tambem é possível fazer:
@lista = ( qw|item1 item2|, 'item3', qw|item4|, $item5 );

Lembre-se, um scalar é sempre uma coisa apenas. E a lista pode ser mais de uma coisa.

Então por exemplo, se chamar um método e passar um array (de-referenciado) para esse método, o que acontece ?

my @l=(qw|aa bb cc|);   #ARRAY
my $test = sub {                            # sub é tipo function no javascript. é declaração de método. neste caso joguei o método na variável $test
    my ( $um, $dois, $tres ) = @_ ;
    warn $um;
    warn $dois;
    warn $tres;
} ;
$test->( @l ); # chamada passando o array de-referenciado

saida: cada item da lista caiu como um argumento. Pois a lista @l tinha 3 strings "aa" "bb" "cc".

aa at -e line 1.
bb at -e line 1.
cc at -e line 1.

Agora, ao invés de passar a lista, vou passar um array

my $l=[qw|aa bb cc|];     #ARRAY ref
my $test = sub {
my ( $um, $dois, $tres ) = @_ ;
    warn $um;
    warn $dois;
    warn $tres;
} ;
$test->( $l );

#saída
ARRAY(0x94d4068) at -e line 1.           # recebeu o array no primeiro argumento, pois array é uma referencia e apenas uma coisa.
Warning: something's wrong at -e line 1. # não recebeu nada no segundo argumento
Warning: something's wrong at -e line 1. # não recebeu nada no terceiro argumento

* observe tambem o argumentos: my ($um,$dois,$tres) .. eles tambem são uma lista. Ou seja, my (...) = @_; assim como @lista = (...);

Para saber o tamanho de um array de-referenciado:

my @lista = ( 1,2,3 );
my $tamanho = @lista;
warn $tamanho; #3

ou $tamanho = scalar @lista; # nem sempre é obrigatório o uso de parentêsis
                             # na chamada dos comandos. Poderia ter chamado
                             # assim: $tam = scalar( @lista ); c/parentesis
ou $tamanho = $#lista;

e para array refs:

my $lista = [ 1,2,3 ];
length $lista; #3

Acesso aos valores da lista:

my $item    = $minha_lista[2]         #terceiro item da lista
$minha_lista[2] = "bla"               #altera terceiro valor

Foreach loop em lista:

my @lista = qw(|aa bb cc|);
foreach my $item ( @lista ) {
    warn $item;
}

Foreach loop em array. Para fazer um loop em um array, é necessário de-referenciar o array em lista, pois o foreach exige uma lista como argumento para trabalhar.

$ perldoc perlsyn #procure por foreach, while etc.

my $arr = [ qw|  aa bb cc | ];
foreach my $item ( @{  $arr  } ) {
    warn $item;
}

de-rerefenciando um array:

my @array2 = @{  $array1  };
use DDP; warn p @array2;

referenciando um array:

my $arr = \@array2;
use DDP; warn p $arr;

Loops

For com lista:

my @l=(qw|aa bb cc|); 
for ( my $i =0; $i < @l; $i++ ) { 
    warn $l[ $i ] ;
} 

For com array:

my $l=[qw|aa bb cc|]; 
for ( my $i =0; $i < @{$l}; $i++ ) { 
    warn $l->[$i];    #opcao 1 para printar o valor
    warn @{$l}[$i] ;  #opção 2 para printar o valor
}

While loop com lista:

Tendo em vista que o while espera uma expressão, ele só vai executar se essa expressão resultar positiva. Vou usar o comando shift que retira e retorna o primeiro item da lista, reduzindo a lista em menos 1 item. E quando terminar de retornar os valores, a expressão vai resultar negativo e o while não será executado.

my @lista = (qw| aa bb cc| ); 
while ( my $item = shift @lista) { 
    warn $item        # <-- todos os comando precisam obrigatoriamente de ponto e vírgula, menos o último comando do bloco.
}

continue

O while pode fazer uso de blocos continue. Sempre que o comando next é utilizado, o processamento é desviado para o bloco 'continue' se este existir, ex:

perl -e '$\="\n"; my $var = 1; while ( $var++ < 10 ) { print $var; next if $var < 7; print "primeiro" } continue { print "dentro do continue" }; print "continuando ...."'
2
dentro do continue
3
dentro do continue
4
dentro do continue
5
dentro do continue
6
dentro do continue
7
primeiro
dentro do continue
8
primeiro
dentro do continue
9
primeiro
dentro do continue
10
primeiro
dentro do continue
continuando ....

last

O comando last pode ser usado dentro de um while. Quando utilizado o processamento do while é abortado, podendo cair no block do continue se estiver presente:

perl -e '$\="\n"; my $var = 1; while ( $var++ < 10 ) { print $var; last if $var > 4; print "primeiro" } continue { print "dentro do continue" }; print "continuando ...."'
2
primeiro
dentro do continue
3
primeiro
dentro do continue
4
primeiro
dentro do continue
5
continuando ....

for

for ( var $i = 0; $i < 10; $i++ ) {
    print $i;
}

foreach

foreach my $fruta ( qw|maçã banana pera uva| ) {
    print $fruta
}

goto

Com o comando goto é possível executar a partir do rótulo ou blocos rotulados, ex:

PARTE_A:{
    warn "PARTE_A 1/3";
    warn "PARTE_A 2/3";
    warn "PARTE_A 3/3";
}

PARTE_B: {
    warn "PARTE_B 1/3";
    warn "PARTE_B 2/3";
    warn "PARTE_B 3/3";
    goto PARTE_C;
}


goto PARTE_A;
goto PARTE_B;


PARTE_C :{
    warn "PARTE_C";
    goto PARTE_D;
}

PARTE_D:

warn "PARTE_D 1/2";
warn "PARTE_D 2/2";

PARTE_E:

warn "PARTE_E 1/2";
warn "PARTE_E 2/2";

#   PARTE_A 1/3 at t.pl line 1.
#   PARTE_A 2/3 at t.pl line 3.
#   PARTE_A 3/3 at t.pl line 4.
#   PARTE_B 1/3 at t.pl line 8.
#   PARTE_B 2/3 at t.pl line 9.
#   PARTE_B 3/3 at t.pl line 10.
#   PARTE_C at t.pl line 19.
#   PARTE_D 1/2 at t.pl line 26.
#   PARTE_D 2/2 at t.pl line 27.
#   PARTE_E 1/2 at t.pl line 29.
#   PARTE_E 2/2 at t.pl line 32.

Map, o map permite trabalhar com cada item da lista. É igual ao foreach, mas ele pode ser encadeado com outros maps ou greps pois ambos esperam uma lista como entrada e ambos retornam uma lista como resultado.

my @pessoas = (
    {
        nome  => 'Jão',
        idade => 'dimenor',
        ensino=> 'médio',
    },
    {
        nome  => 'Mary',
        idade => 21,
        ensino=> 'superior',
    },
    {
        nome  => 'Janet',
        sobrenome=>'Silva',
        idade => 25,
        ensino=> 'superior',
    },
);

@pessoas = map {
    if ( ! $_->{ sobrenome } ) {
        $_->{ sobrenome }  = undefined;
        $_->{ status }     = "SEM-SOBRENOME" ;
    }
    $_; # valor que será retornado. Não precisa colocar o 'return'
    # return $_; #outra opção seria colocar o return, mas não é obrigatório
} @pessoas;
use DDP; warn p @pessoas;

grep, posso usar o grep para filtrar os maiores de idade dentro de uma lista de pessoas. Esta é uma lista de hashes onde cada hash representa uma pessoa. Usarei o grep para filtrar e obter todas as pessoas que não são dimenor.

my @pessoas = (
    {
        nome  => 'Jão',
        idade => 'dimenor',
        ensino=> 'médio',
    },
    {
        nome  => 'Mary',
        idade => 21,
        ensino=> 'superior',
    },
    {
        nome  => 'Janet',
        idade => 25,
        ensino=> 'superior',
    },
);

my @maior_de_idade = grep {  $_->{ idade }  ne 'dimenor' } @pessoas;
use DDP; warn p @maior_de_idade;

Inserindo e removendo items de arrays e listas

Sempre que trabalhamos com arrays, ou pilhas ou filas precisamos inserir itens no começo ou final do array, e as vezes, retirar itens do começo ou final. Os comandos para fazer isso são:

perldoc -f push
perldoc -f pop
perldoc -f shift
perldoc -f unshift

Trabalham com o final do array

push: Insere ao final do array. perldoc -f push
my @array  = ( "um" , "dois" );
my $total = push @array, ( "tres", "quatro" );
use DDP; warn p @array;
warn $total;
# saida
#   [
#       [0] "um",
#       [1] "dois",
#       [2] "tres",
#       [3] "quatro"
#   ] at t.pl line 5.
#   4 at t.pl line 6.

pop:  Retorna e retira o último valor do array.
my @array  = ( "um", "dois" );
my $item = pop @array;
warn $item;
use DDP; warn p @array;
# saida
#   dois at t.pl line 3.
#   [
#       [0] "um"
#   ] at t.pl line 4.

Trabalham com o início do array

shift:  Retira e retorna o primeiro item do array
my @array       = ( qw|um dois tres quatro| );
my $primeiro    = shift @array;
warn $primeiro;
use DDP; warn p @array;
# saida
#   um at t.pl line 3.
#   [
#       [0] "dois",
#       [1] "tres",
#       [2] "quatro"
#   ] at t.pl line 4.

my @array       = ( qw|um dois tres quatro| );
my $total       = unshift  @array, ("menos 1","zero");
use DDP; warn p @array;
warn $total;
# saida
#   [
#       [0] "menos 1",
#       [1] "zero",
#       [2] "um",
#       [3] "dois",
#       [4] "tres",
#       [5] "quatro"
#   ] at t.pl line 3.
#   6 at t.pl line 4.

Slices

perldoc -f perldata

Slices permite acessar através de um único comando muitos elementos de um array ou hash sem a necessidade de um 'for' loop. Ex:

my @pessoas = ( qw| Joao Maria Antonio José Mariana Jéssica | );
my ($pessoa2, $pessoa1) = @pessoas[2,1];
    warn $pessoa2; warn $pessoa1;
#saida
#Antonio at t.pl line 4.
#Maria at t.pl line 4.
my @outros = @pessoas[2..5];
    warn @outros;
#saida
#AntonioJoséMarianaJéssica at t.pl line 6.

my %formulario = (  
    nome => 'João', 
    sexo => 'Masculino' 
);
my ( $nome, $sexo ) = @formulario{"nome","sexo"};
    warn $nome;
    warn $sexo;
#saida
#João at t.pl line 13.
#Masculino at t.pl line 14.

Tambem é possível setar valores usando slices

my @pessoas    = ();
@pessoas[1..5] = (qw|joao maria alberto francisco naldo fernando|);
use DDP; warn p @pessoas;
#saida
#   [
#       [0] undef,
#       [1] "joao",
#       [2] "maria",
#       [3] "alberto",
#       [4] "francisco",
#       [5] "naldo"
#   ] at t.pl line 4.

my %form    = ();
@form{'nome','sobrenome'} = ("João","da Silva");
#saida
#   {
#       nome        "João",
#       sobrenome   "da Silva"
#   } at t.pl line 9.
#   use DDP; warn p %form;

Referenciando e De-referenciando

Resumo: para passar a referência normalmente usa-se a \ antes da variável. E para de-referenciar, usa-se o tipo{}, @{ $var } para de-referenciar $var em lista e %{ $var } para de-referenciar $var em lista-ordenada. É possível referenciar e dereferenciar a la vontê.

Sempre que o código tiver uma lista e precisar passar pra uma função que espera um array, é necessário passar a referência da lista que vira array para o método. É possível passar a lista, mas provavelmente não é isso que você precisa.

my @lista                         = ( qw| aa bb cc | );
use DDP; warn p @lista;
my $lista_array_ref               = \@lista;    #\ retorna a referência
use DDP; warn p $lista_array_ref; warn ref $lista_array_ref;
my @lista_dereferenciada          = @{  $lista_array_ref  };
use DDP; warn p @lista_dereferenciada;

Agora com lista ordenada:

my %formulario = (
    nome    => 'Jão',
    idade   => 'dimenor'
);
use DDP; warn p %formulario;
my $form       = \%formulario;
warn p $form; warn ref $form;
my %form_dereferenciado = %{  $form  };
warn p %form_dereferenciado;

De-referenciar quer dizer: mudar o contexto dos dados. Ou seja, eu posso ter uma função que aguarda um array como argumento... mas, eu tenho uma lista de valores e preciso passar essa lista pra função que espera um array. Nesse momento eu posso transformar a lista em array para passar pra função.

my @lista = ( qw| aa bb cc dd ee ff| );
my $funcao = sub {
    my ( $arr ) = @_;     #recebe um scalar
    use DDP; warn p $arr;
};

$funcao->(  \@lista  );   #

Ao passar uma lista por referencia ela é transformada em ARRAY(scalar) para ser recebida apenas como 1 único argumento. E para transformá-la em lista novamente é necessário fazer uma de-referencia.. que funciona da seguinte maneira:

my @lista = ( qw| item1 item2 item3 | );
$algo->passar( \@lista, "outra coisa" ); #passou @lista por referencia

e por sua vez o método passar recebe:

sub passar {
    my ( $lista, $outra_coisa ) = @_;
    use DDP;
    warn p $lista;
    warn ref $lista;            #ARRAY
    my @lista = @{  $lista  };  # Aqui é feita a de-referência de scalar array para lista
    warn p @lista;
}

E, se tentar fazer:

warn ref @lista;

vai dar erro pois @lista são várias coisas. assim como %hash; Enquanto variáveis scalares são apenas uma única coisa.

hash e lista associativa

Hashes tambem podem ser definidos com scalares ou listas ordenadas.

hash scalar:

my $item = {
    nome    => "jão",
    idade   => "dimenor"
};

hash com lista associativa:

my %item = (
    nome    => "jão",
    idade   => "dimenor"
);

%item é um hash.

A lista associativa é:

(
    nome    => "jão",
    idade   => "dimenor"
)

Para transformar um %item em $item, é só passar a referencia (\). Ou seja,

$ perl -e 'use strict; use warnings; my %x=(aa=>1,bb=>2); my $h = \%x; use DDP; warn p $h ; warn ref $h; '

Assim que se passa um hash por referência. Ou seja, se vc tem um %algo e precisa passar para um método, você terá que passar a referência disso: \%algo e na outra ponta vai chegar como scalar: $algo e esse scalar vai ser do tipo HASH. Que pode ser verificado através do comando: warn ref $algo.

Para introspectar e visualizar o hash:

use DDP;
warn p $item;
wran p %item;

lista, lista associativa, arrays e hashes

A lista é representada por uma variávei inicializando com "@". Uma lista são items dentro de parentesis, separados por virgula. Ex.

my @lista = (
    "nome",
    "jão",
    "idade",
    "dimentor"
);

Uma lista associativa, apenas altera o entendimento da lista e a transforma em algo parecido com um hash. Ou seja, se eu alterar a lista acima para uma lista associativa, terei o resultado:

my %lista_associativa = (
    "nome"  => "jão",
    "idade" => "dimentor"
);

Ou seja, associou os itens impares com os itens pares.

Tambem é possível alterar entre uma ou outra, a diferença delas esta nos @ e %. Logo é possível fazer:

my %lista = @lista; #e vice versa

O % e @ vai alterar o comportamento se é aplicada lista ou array associativo. No final eles são bem similares e mudam de acorco com % ou @.

Condição para adicionar ou não item em lista, hash e array

Algo muito bacana pode ser usado em perl, que é colocar um ternário como um item de uma lista, hash ou array.

use DDP;

primeiro um arrau com um item que é uma lista vazia

my $array = [
  'um', 
  'dois', 
  (),       # este valor é igual a lista em branco, que é igual a nada neste caso.
            # pois () é sempre uma lista de itens, então aqui é uma lista de itens
            # vazia neste caso.. consequentemente acaba não contando como item nenhum.
  'tres'
];

warn p $array;
warn 'Total de itens: ' .scalar @$array;
#   \ [
#       [0] "um",
#       [1] "dois",
#       [2] "tres"
#   ] at t.pl line 10.
#   Total de itens: 3 at t.pl line 11.

agora um array que tem uma condição para decidir o valor do terceiro item

my $array2 = [
  'um', 
  'dois', 
  ( 1 == 1 ) 
  ? ( 'sim' ) 
  : ( 'não' ),       # este valor é igual a array em branco, que é igual a nada neste caso.
  'tres'
];
warn p $array2;
warn 'Total de itens: '. scalar @$array2;
#   \ [
#       [0] "um",
#       [1] "dois",
#       [2] "sim",
#       [3] "tres"
#   ] at t.pl line 22.
#   Total de itens: 4 at t.pl line 23.

agora, um outro array com condição e retorna 3 itens sim caso a condição seja positiva

my $array3 = [
  'um', 
  'dois', 
  ( 1 == 1 ) 
  ? ( 'sim', 'sim', 'sim' ) 
  : ( 'não' ),       # este valor é igual a array em branco, que é igual a nada neste caso.
  'tres'
];
warn p $array3;
warn 'Total de itens: '. scalar @$array3;
#   \ [
#       [0] "um",
#       [1] "dois",
#       [2] "sim",
#       [3] "sim",
#       [4] "sim",
#       [5] "tres"
#   ] at t.pl line 35.
#   Total de itens: 6 at t.pl line 36.

um a lista com condição para decidir qual item entra na lista

my @list = (
  'um',
  'dois',
  ( 3 == 3 ) ? ( 'tres' ): ( 'inválido' ),
  'quatro',
);
warn p @list;
warn 'Total de itens: ' . scalar @list;
#   [
#       [0] "um",
#       [1] "dois",
#       [2] "tres",
#       [3] "quatro"
#   ] at t.pl line 47.
#   Total de itens: 4 at t.pl line 48.

Tambem é possível usar o () para setar valor nenhum:

my @list = (
  'um',
  'dois',
  ( 3 == 3 ) ? ( ): ( ),
  'quatro',
);
warn p @list;
warn 'Total de itens: ' . scalar @list;
#   [
#       [0] "um",
#       [1] "dois",
#       [2] "quatro"
#   ] at t.pl line 9.
#   Total de itens: 3 at t.pl line 10.

ou setar vários itens de uma só vez

my @list2 = (
  'um',
  'dois',
  ( 3 == 3 ) ? ( 'tres1', 'tres2', 'tres3' ): ( 'inválido' ),
  'quatro',
);
warn p @list2;
warn 'Total de itens: ' . scalar @list2;
#   [
#       [0] "um",
#       [1] "dois",
#       [2] "tres1",
#       [3] "tres2",
#       [4] "tres3",
#       [5] "quatro"
#   ] at t.pl line 56.
#   Total de itens: 6 at t.pl line 57.

agora uma lista ordenada

my $idade = 17;
my %lista_ordenada = (
  nome => 'João',
  ( $idade < 18 )
  ? ( idade => 'dimenor' )
  : ( idade => 'dimaior' )
);
warn p %lista_ordenada;
#   {
#       idade   "dimenor",
#       nome    "João"
#   } at t.pl line 67.

um hash

my $hash = {
    nome => "jão",
    ( $idade  < 18  ) 
    ? ( idade => 'dimenor' ) 
    : ( idade => 'dimaior' ),
};
warn p $hash;
#   \ {
#       idade   "dimenor",
#       nome    "jão"
#   } at t.pl line 119.

Contexto

Tambem é possível transformar os resultados da segunte maneira:

Primeiro vou criar um array com uma lista de palavaras:

my     @palavras  = ( qw| nome primeiro sobrenome segundo idade terceiro| );
warn p @palavras;
#   [
#       [0] "nome",
#       [1] "primeiro",
#       [2] "sobrenome",
#       [3] "segundo",
#       [4] "idade",
#       [5] "terceiro"
#   ] at t.pl line 4.

Agora é possível transformar essa lista em uma lista ordenada e acessá-la como um hash. Esta operação pode ser realizada atraves da mudança de contexto de array para hash. Para fazer isso é possível dizer que ex: %form vai receber o valor de @palavras no contexto de hash %:

my     %form      = %{{@palavras}};
warn p %form;
#   {
#       idade       "terceiro",
#       nome        "primeiro",
#       sobrenome   "segundo"
#   } at t.pl line 8.

É possível transformar de volta em array

my @lista2 = %form;
warn p @lista2;
#   [
#       [0] "nome",
#       [1] "primeiro",
#       [2] "sobrenome",
#       [3] "segundo",
#       [4] "idade",
#       [5] "terceiro"
#   ] at t.pl line 12.

Outra maneira seria usar map que retorna array. Ao colocar colchetes ao redor de um array, acaba gerando um hash: { ..array...}

my $form_hash = { map { $_ } @lista2 };
warn p $form_hash;
#   \ {
#       idade       "terceiro",
#       nome        "primeiro",
#       sobrenome   "segundo"
#   } at t.pl line 16.

my $form_hash = {@lista2};
warn p $form_hash;
#   \ {
#       idade       "terceiro",
#       nome        "primeiro",
#       sobrenome   "segundo"
#   } at t.pl line 16.

Retransforma em lista

my @lista_3 = %{ $form_hash };
warn p @lista_3;
#   [
#       [0] "idade",
#       [1] "terceiro",
#       [2] "sobrenome",
#       [3] "segundo",
#       [4] "nome",
#       [5] "primeiro"
#   ] at t.pl line 20.

É possível encadear comandos que usam o mesmo tipo retornado, por exemplo:

perl -e ' my @l=(qw|aa bb cc dd|); my $tmp={}; my $l2= {reverse reverse sort grep { if ( ! exists $tmp->{$_} ) { $tmp->{$_} =1 ; } } map { $_ => $_ } split ",", (join (",", @l))}; use DDP; warn p $l2;  '

neste caso os comandos reverse, sort, grep, map e split esperam uma lista como entrada e tambem retornam uma lista, logo, eles podem ser encadeados. Todos eles estão dentro de {}, transformando a lista retornada em hash: $l2 = { ...lista retornada ... }

Seria o mesmo que fazer:

use DDP;
my     @palavras  = ( qw| nome primeiro sobrenome segundo idade terceiro| );
my     $form      = {@palavras};
warn p $form;
#   {
#       idade       "terceiro",
#       nome        "primeiro",
#       sobrenome   "segundo"
#   } at t.pl line 8.

Juntando listas

my @lista_a = (qw|item1 item2 item3|);
my @lista_b = (qw| item4 item5 |);

my @tudo    = ( @lista_a, @lista_b, () );
use DDP ; warn p @tudo;
#   [
#       [0] "item1",
#       [1] "item2",
#       [2] "item3",
#       [3] "item4",
#       [4] "item5"
#   ] at t.pl line 5.

Juntando hashes

my %formulario_parte_a = ( 
    nome        => 'João',
    sobrenome   => 'Silva'
);
my %formulario_parte_b = (
    linguagens => [ qw|perl javascript| ],
    ensino     => 'superior'
);

my %form = ( %formulario_parte_a, %formulario_parte_b );
use DDP ; warn p %form;
#   {
#       ensino       "superior",
#       linguagens   [
#           [0] "perl",
#           [1] "javascript"
#       ],
#       nome         "João",
#       sobrenome    "Silva"
#   } 

Referência circular com variáveis

O exemplo a seguir mostra declara uma variável %a, e depois faz referência a ela mesma dentro dela mesma, e isso resulta em uma referência circular infinita. No exemplo, %a->{b} faz referência a %a. Não faz muito sentido fazer isto, mas é possível.

$ perl -e ' my %a; my $a="AAA"; my @a = ( $a,\%a); %a = ( "ex"=> \@a, "b" => \%a ); use Data::Dumper; warn Dumper %a;' 
$VAR1 = 'b';
$VAR2 = {
          'b' => $VAR2,
          'ex' => [
                    'AAA',
                    $VAR2
                  ]
        };
$VAR3 = 'ex';
$VAR4 = $VAR2->{'ex'};

iprime valor de %a->{b}

$ perl -e ' my %a; my $a="AAA"; my @a = ( $a,\%a); %a = ( "ex"=> \@a, "b" => \%a ); use Data::Dumper; warn Dumper %a->{b};' 
Using a hash as a reference is deprecated at -e line 1.
$VAR1 = {
          'ex' => [
                    'AAA',
                    $VAR1
                  ],
          'b' => $VAR1
        };

imprime valor de %a->{b}->{b}

$ perl -e ' my %a; my $a="AAA"; my @a = ( $a,\%a); %a = ( "ex"=> \@a, "b" => \%a ); use Data::Dumper; warn Dumper %a->{b}->{b};' 
Using a hash as a reference is deprecated at -e line 1.
$VAR1 = {
          'b' => $VAR1,
          'ex' => [
                    'AAA',
                    $VAR1
                  ]
        };

imprime valor de %a->{b}->{b}->{b}

$ perl -e ' my %a; my $a="AAA"; my @a = ( $a,\%a); %a = ( "ex"=> \@a, "b" => \%a ); use Data::Dumper; warn Dumper %a->{b}->{b}->{b};' 
Using a hash as a reference is deprecated at -e line 1.
$VAR1 = {
          'b' => $VAR1,
          'ex' => [
                    'AAA',
                    $VAR1
                  ]
        };

imprime valor de %a->{b}->{b}->{b}->{b}

$ perl -e ' my %a; my $a="AAA"; my @a = ( $a,\%a); %a = ( "ex"=> \@a, "b" => \%a ); use Data::Dumper; warn Dumper %a->{b}->{b}->{b}->{b};' 
Using a hash as a reference is deprecated at -e line 1.
$VAR1 = {
          'ex' => [
                    'AAA',
                    $VAR1
                  ],
          'b' => $VAR1
        };

coma e Fat-coma, virgula e virgula gorda

Existe a virgula e a virgula-gorda. Ambas são a mesma coisa, só muda para representação visual.

Vírgula / Coma

,

Vírgula-Gorda / Fat-coma

=>

Se eu escrever uma lista com => ou , vai dar na mesma... mas => é bastante utilizado em hashes para indicar uma 'associação'

Ou seja,

my @lista = ( 1, 2, 3 )

é a mesma coisa que

my @lista = ( 1 => 2 => 3 );

para fazer a prova é possível usar o Data::Printer, que permite printar o objeto na tela. ( introspection )

my @lista = ( 1, 2, 3 );     use DDP; warn p @lista;
my @lista = ( 1 => 2 => 3 ); use DDP; warn p @lista;

Regex - Expressão Regular

A linguagem perl é muito talentosa para trabalhar com textos. Por esse motivo seu engine de regex é o mais avançado entre as linguagens, e virou referência que serve como padrão de compatibilidade. O termo para isso é: PCRE ( Perl Compatible Regular Expressions ). Toda linguagem que implementa um engine de regex bom tem que ser PCRE.

$ perldoc perlre

O operador de expressão regular em perl:

=~

e o negador (não match)

!~

É utilizado da seguinte maneira:

my $texto = "bla aa bb cc dd aa";
$texto =~ m/aa/gi;                  #m=match
$texto =~ s/isto/por aquili/gi      #s=substituição
                                    gi são os modificadores, i é case insensitive
                                    e g=global do começo ao final da string

$texto =~ m/(.+)algo(?<nome_do_grupo>.+)/g   #um grupo não nomeado e um grupo nomeado

print $+{nome_do_grupo}

if ( $text =~ m!bla! ) {
    ...
}

é possível usar exclamação, barras, colchetes na regex, ex, todas estas representam a mesma coisa:

m//g m!!g m{}g

s///g s!!!g s{}{}g

Tem tudo no perlre.

Matches

As regex retornam por padrão uma lista de matches. Então é possível trabalhar da seguinte maneira

Automaticamente $nome recebe o valor do primeiro grupo de matches e $idade recebe o valor do segundo match

my   $texto = "Jão dimenor";
my ( $nome, $idade ) = $texto =~ m/(.+) (.+)/ig; 
warn $nome;
warn $idade;

$nome recebe o valor do primeiro grupo de capturas, $idade recebe valor do segundo. $total recebe o total de itens na lista à direita;

my   $total = ( $nome, $idade ) = $texto =~ m/(.+) (.+)/ig;
warn $total;

Esta opção permite contar quantas matches ocorreram, e grupos nomeados

my   $total =()= $texto =~ m/(?<nome>.+) (?<idade>.+)/ig;
warn $total;
warn $nome;
warn $idade;

A opção a seguir permite parsear uma string separada por virgula e transformar em hash

perl -e '  my $line = "xxx-XXX=\"Something one\",yyy_YY-Y=\"Something Y\",zzz-zzZZ-ZZ=\"ZZzzZZZ\""; my $object = { $line =~ m#([^,=]+)="([^"]+)"#g }; use Data::Dumper; warn Dumper $object;'

Quantidade de matches

Para saber a quantidade de matches de uma regex, é necessário usar o operador goatse " =()= ".

Para contar o total de vezes que aparece o trecho aa no texto abaixo:

my   $texto = "aa bb cc aa aa aa bb aa cc dd aa";
my   $total_de_aa =()= $texto =~ m!aa!g;
warn $total_de_aa;

Transliteração

Para substituir uma letra por outra, usa-se bastante transliteração. As vezes o pessoal faz com regex, ou sei la como, mas o jeito mais coerente é usar transliteração, ex:

Vamos supor que quero transformar todas as vogais em letras maiúscilas. Posso fazer da seguinte maneira com transliteração:

my $text = "aa bb cc dd ee ff gg";
$text =~ tr/[aeiou]/[AEIOU]/;
warn $text;
#saida: AA bb cc dd EE ff gg

outro uso bem comum é para trocar caracteres acentuados por caracteres sem acento:

my $text2 = "aaá eée iíí çc";
$text2 =~ tr!áãíçé!aaice!;
warn $text2;
#saída: aaa eee iii cc

Pragmas

Um pragma é um módulo que influencia sob alguns aspectos na hora de compilar e executar códigos perl. Dois pragmas muito importantes e bastante usados são o "strict e "warnings". Isso quer dizer que todos as apps e scripts perl devem começar com:

use strict;
use warnings;

Para saber mais sobre pragmas, acesse o perldoc pragma

$ perldoc pragma

No caso do pragma strict, verifica e valida se todas as variáveis foram declaradas. Caso alguma não tenha sido declarada, imprime um erro com o aviso na tela, ex: $ perl -e 'use strict; $var = "bla";' Global symbol "$var" requires explicit package name at -e line 1. Execution of -e aborted due to compilation errors.

use strict

Previne uso de variáveis não declaradas.

$ perldoc strict

use warnings

Pragma para controlar warnings opcionais

$ perldoc warnings

Os pragmas funcionam e conseguem modificar apenas dentro do escopo onde foram declarados.

Instalação de modulos

A instalação de módulos é bastante simples. Com o nome de módulo em mãos, é possível instalar com o comando:

$ cpanm Nome::do::Modulo Nome::de::Outro::Modulo

O cpanm é o instalador de módulos mais indicado hoje em dia. É super simples e fácil de usar e instalar.

cpanm

O cpanm é um instalador de módulos. Ele pode instalar módulos do cpan, ou a partir de arquivos locais, ou de qualquer url, entre outras opções avançadas. O mais comum é usar o comando para instalar um módulo do cpanm com o comando cpanm

O cpanm se identifica como sendo a versão mini da app cpan (instalador padrão de módulos). Mini no sentido de ter pouco código e opções mais refinadas.

$ cpanm Modulo::Bla

Para ver todas as opções

$ cpanm --help

Durante a instalação de um módulo, se houver erro, será apresentado um log junto à mensagem de erro. É importante ler este log, principalmente no final, e localizar a parte onde ocorreu o erro. As vezes um módulo pode ter como pré-requisito/dependência uma lib no sistema.

Por exemplo, para trabalhar com imagens, é sempre bom instalar as libs:

$ sudo apt-get install libpng-dev libgif-dev libjpeg-dev

Para trabalhar com módulos xml, xpath

$ sudo apt-get install libexpat-dev

Etc, se na instalação de alguma app for solicitado essas libs antes de serem instaladas, haverá um erro. Tudo estará escrito nos logs.

Já as dependências de módulos, essa é automaticamente gerenciada para você. Cada módulo traz consigo uma lista de dependências, e ao instalar uma, as dependência são instaladas recursivamente automaticamente.

Todos os módulos perl vem com testes, e alguns módulos mais usados as vezes tem muitos muitos testes. Testar tudo pode demorar um pouquinho. Os apressados podem usar a opção

cpanm --notest  Nome::Modulo  #só use isto se tiver certeza que usa máquina já tem os pre-requisitos instalados

Outra opção é forçar uma instalação que tenha falha durante a execução dos testes.

cpanm --force   Nome::Modulo

cpan

O cpan é a interface oficial para interagir com o cpan. O cpan existe faz mais de 20 anos e através dela é possível instalar módulos. A primeira vez que é executado, a app pede algumas configurações. Uma das opções aceita a resposta padrão para tudo, recomendo. Feito isso é só instalar os módulos da seguinte maneira:

$ cpan Modulo::Um Modulo::Dois

Criação de módulos

Existem algumas ferramentas que auxiliam a criação de esqueleto de módulos. Ao mesmo tempo estas ferramentas se mantem atualizadas e criam o esqueleto seguindo os padrões corretos para a criação de um módulo perl.

Um módulo perl, inclúi os arquivos fonte relacionados ao módulo, esses arquivos podem estar listados em um arquivo chamado MANIFEST. Informações sobre dependência são essenciais pois ao subir um módulo no cpan, o robozinho do smoke-tester vai tentar rodar os testes da app e vai dar erro de dependência se ela não tiver sido listada como pré-requisito. A documentação é bastante importante, siga os outros módulos... e a documentação é feita no formato pod.

$ perldoc pod

Os testes são essenciais e ficam dentro do diretório t/ da applicação.

O nome do módulo tem que ser auto informativo. Não crie nomes estranhos nada a ver. Pesquise outros módulos para se familiarizar com as nomenclaturas. Peça ajuda no irc, ou entre para uma lista de perl mongers no brasil.

Pesquise se já existe um módulo que faz o que o projeto precisa. Provavelmente já existe um módulo que faz o trabalho que já foi amplamente testado.

Dist::Milla

O Dist::Milla é um gerador de esqueleto de módulos perl. Com ele é possível gerar um módulo em branco e seus arquivos base.

Após instalar o Dist::Milla ( cpanm Dist::Milla ) , execute o setup:

$ milla setup

depois é só criar módulos com o comando:

$ milla new Nome::Do::Modulo

$ milla help

Existem outros geradores de esqueleto.

Execução de scripts

one-liners

One-liners são executados através da linha de comando. E tem apenas uma linha. Para executar um trecho de código diretamente na linha de comando, é só jogar o código dentro do comando: perl -e '...codigo...' ex:

perl -e ' use strict; use warnings; my $var = 10; warn $var+1; '

Executando aplicações com versões específicas de módulos

Quando um script/app é executado, este provavelmente tem dependências que precisam estar instaladas para rodar um mero: "perl script.pl". No entanto, é possível executar um script sem ter instalado os módulos necessários para execução. É claro que o módulo tem que estar presente, mas ele pode estar presente dentro de qualquer diretório. É necessário apenas passar esse diretório como um argumento na hora de executar o script/app perl.

Esta facilidade é muito boa para aplicações em produção, pois é possível manter repositórios em sincronia e apenas gracefully restartar os serviços indicando o local onde estáo os fontes das dependências do scrpit. Suponha que meu_modulo.pl requer o Meu::Modulo. Mas eu ainda não subi Meu::Modulo para o cpan, e tambem não tenho ele instalado. Meu::Modulo está num diretório local na minha máquina. Então eu executaria o meu_modulo.pl passando o local onde estão os fontes requeridos para a execução, da seguinte maneira:

perl -I../Meu-Modulo/lib/ -I../Outro-Modulo/lib meu_modulo.pl

Para desenvolvimento tambem é interessante usar módulos dessa maneira, caso contrário seria necessário re-instalar os módulos após cada alteração. Isso seria non-sense.

Ao usar a opção -I, behind the scenes os diretórios são inseridos em @INC.

@INC %INC

Entre as variáveis especiais no perl, existe a @INC e %INC. Elas são o caminho das pedras para o interpretador encontrar os módulos na hora de executar uma aplicação perl. E tambem serve como caminho para o desenvolvedor ver quais módulos estão carregados antes de adicionar uma 'nova/desnecessária' dependência.

O interpretador do perl é compilado com um @INC padrão, que inclúi o diretório base onde os módulos são instalados. Para alterar o padrão é possível usar: http://search.cpan.org/perldoc?INSTALL#otherlibdirs

$ env -i perl -V   # env -i ignora PERL5LIB env var
$ perl -V          # para perlbrew

Para visualizar o conteúdo de @INC ou %INC, dentro do seu script escreva:

use Data::Dumper; 
warn Dumper(\@INC);
   

tambem é possível fazer um one-liner:

$ perl -e 'use Data::Dumper; warn Dumper(\@INC)'
# saída
$VAR1 = [
  '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/site_perl/5.17.7/x86_64-linux',
  '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/site_perl/5.17.7',
  '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/x86_64-linux',
  '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7',
  '.'
];

Agora, o conteúdo do %INC

$ perl -e 'use Data::Dumper; warn Dumper(\%INC) 
# saída 
$VAR1 = {
  'Carp.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/Carp.pm',
  'overload.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/overload.pm',
  'bytes.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/bytes.pm',
  'warnings/register.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/warnings/register.pm',
  'utf8.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/utf8.pm',
  'strict.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/strict.pm',
  'unicore/lib/Perl/_PerlIDS.pl' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/unicore/lib/Perl/_PerlIDS.pl',
  'utf8_heavy.pl' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/utf8_heavy.pl',
  'warnings.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/warnings.pm',
  'overloading.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/overloading.pm',
  'XSLoader.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/XSLoader.pm',
  'Data/Dumper.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/x86_64-linux/Data/Dumper.pm',
  'Exporter.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/Exporter.pm',
  'constant.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/constant.pm',
  'vars.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/vars.pm',
  'unicore/Heavy.pl' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/unicore/Heavy.pl'
};

agora, vou fazer uso de alguns módulos e printar o %INC, é possível perceber que os módulos usados estão presentes no %INC:

$ perl -e 'use MIME::Base64; use Plack; use utf8; use Digest::SHA1; use Data::Dumper; warn Dumper(\%INC) '
# saída
$VAR1 = {
  'utf8_heavy.pl' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/utf8_heavy.pl',
  'warnings/register.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/warnings/register.pm',
  'unicore/lib/Perl/_PerlIDS.pl' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/unicore/lib/Perl/_PerlIDS.pl',
  'overload.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/overload.pm',
  'Carp.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/Carp.pm',
  'vars.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/vars.pm',
  'Digest/base.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/Digest/base.pm',
  'DynaLoader.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/x86_64-linux/DynaLoader.pm',
  'unicore/Heavy.pl' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/unicore/Heavy.pl',
  'Data/Dumper.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/x86_64-linux/Data/Dumper.pm',
  'constant.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/constant.pm',
  'utf8.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/utf8.pm',
  'Config.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/x86_64-linux/Config.pm',
  'Plack.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/site_perl/5.17.7/Plack.pm',
  'MIME/Base64.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/x86_64-linux/MIME/Base64.pm',
  'XSLoader.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/XSLoader.pm',
  'Digest/SHA1.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/site_perl/5.17.7/x86_64-linux/Digest/SHA1.pm',
  'overloading.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/overloading.pm',
  'bytes.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/bytes.pm',
  'warnings.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/warnings.pm',
  'Exporter.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/Exporter.pm',
  'strict.pm' => '/home/dev/perl5/perlbrew/perls/perl-5.17.7/lib/5.17.7/strict.pm'
};

Se souber o que está fazendo, é possível manipular o @INC. Mas isso tem que ser feito dentro de um bloco: BEGIN {} que precede a diretiva 'use Modulo::Nome'.

BEGIN {
  unshift @INC, $directory ;
  push    @INC, $directory ;
}

print vs warn vs say

$ perldoc -f warn
$ perldoc -f print
$ perldoc -f say

Para imprimir mensagens existe basicamente 3 alteranativas. As mais comuns e tambem mais antigas são print e warn. O say exige declaração do uso da feature say.

As 3 servem para printar mensagens na tela, no entanto, o warn printa em STDERR enquanto print e say printam para STDOUT.

ps. em unix as aplicações podem receber dados do STDIN (ex teclado) e enviar dados para STDOUT (ex terminal) e STDERR ( ex terminal tambem, mas como erro) mais em: http://en.wikipedia.org/wiki/Standard_streams

Print e say, tentando capturar de STDERR ( 2> ), não vai dar certo pois por padrão essas funções mandam pra STDOUT ( > ):

$ rm log; perl -e 'use feature "say" ; warn "a" ;   ' 2> log && echo "cat log:" && cat log   # usa 2> pra capturar STDERR
$ rm log; perl -e 'use feature "say" ; warn "a" ;   ' 2> log && echo "cat log:" && cat log   # usa 2> pra capturar STDERR

agora sim, vai conseguir capturar de STDOUT:

$ rm log; perl -e 'use feature "say" ;  say "a" ;   '  > log && echo "cat log:" && cat log   # usa > pra capturar STDOUT
$ rm log; perl -e '                   print "a" ;   '  > log && echo "cat log:" && cat log   # usa > pra capturar STDOUT

O warn joga a saída pra STDERR

$ rm log; perl -e 'use feature 'say' ; warn "a" ;   ' 2> log && echo "cat log:" && cat log   # usa 2> pra capturar STDERR

se tentar capturar a saída do warn na STDOUT, não vai conseguir pois o warn sempre joga pra STDERR

$ rm log; perl -e 'use feature 'say' ; warn "a" ;   '  > log && echo "cat log:" && cat log   # printa em branco 

tambem é possível forçar o print pra imprimir em STDERR:

$ print STDERR "texto";     # e pode ser capturado com 2> 
$ print STDOUT "texto";     # e pode ser capturado com >

Introspecção/Inspeção de objectos:

A introspecção de objetos é algo essencial para poder debugar e entender o que se passa nas aplicações. Outras vezes ajuda para inspecionar um objeto e lembrar o nome daquele método.

Existem ferramentas bacaninhas para fazer esse tipo de análise, vou listar três bastante utilizadas. Eu uso mais o Data::Printer. Funciona muito bem e foi criada por um conterrâneo brazileiro. O Data::Printer aceita cores e customizações que possibilitam aumentar ou restringir o quê imprimir de um objeto na hora da inspeção. Mostra os métodos publicos e privados, isso é bem útil durante o uso de um módulo.

O uso é simples, no meio do código você mete um warn para printar alguma variável, ex:

.... codigo ....
    my $item = Some::Thing::Nice->new();
    use DDP; warn p $item;
.... codigo ....

Vou mostrar exemplos com objetos simples, tipo hash no entanto estes Dumpers podem ser usados em objetos de verdade.

Data::Dumper

O Data::Dumper vem instalado por padrão junto com o perl.

perl -e 'my $var={ a=>[1,2,3],x=>1, y=>2, z=>"bla"}; use Data::Dumper; print Dumper( $var );'
$VAR1 = {
          'y' => 2,
          'z' => 'bla',
          'x' => 1,
          'a' => [
                   1,
                   2,
                   3
                 ]
        };

Data::Dump::Streamer

perl -e 'my $var={ a=>[1,2,3],x=>1, y=>2, z=>"bla"}; use Data::Dump::Streamer; print Dump( $var );'
$HASH1 = {
           a => [
                  1,
                  2,
                  3
                ],
           x => 1,
           y => 2,
           z => 'bla'
         };

Data::Printer

perl -e 'my $var={ a=>[1,2,3],x=>1, y=>2, z=>"bla"}; use DDP; print p $var ;'
\ {
    a   [
        [0] 1,
        [1] 2,
        [2] 3
    ],
    x   1,
    y   2,
    z   "bla"

Arquivos

Para trabalhar com arquivos em perl, é necessário abrir um arquivo, e obter seu conteúdo.

O conteúdo pode ser obtido de muitas maneiras e depende de caso a caso. Por exemplo, se estiver trabalhando com arquivos de log que tem 1 terabyte não será possível abrir o arquivo todo de uma vez em memória. Nesse caso, é necessário ler o arquivo de X em X bytes até chegar no final, ou seja em chunks. As vezes interessa fazer um loop nas linhas do arquivo. Emfim, cada caso é um caso.

Printar todas as linhas de um arquivo

open
open( FH , "<" , "README.pod" ) ; 
while( <FH> ) { 
    print $_ ; 
    print "----------\n"; 
}  

Printar determinado número de bytes de arquvio

Esta opção é muito útil para trabalhar com arquivos gigantes que não podem ser abertos de uma única vez pois não cabem na memória da máquina.

Então é possível fazer open em um arquivo e gerar um filehandle. Posteriormente pode ser executado o comando read para ler N bytes do filehandle.

open( FH , "<" , "README.pod" ) ;
my $bytes = 10;
my $text; while ( read( FH, $text, $bytes ) ) {
    use DDP;
    warn p $text;
}

Outra opção é mexer com a variável especial $/ ( perldoc perlvar ). O exemplo a seguir utiliza-se desta técnica para alterar o $/ por espaços. Agora quando fizer loop no filehandle, vai printar palavras separadas por espaço.

open( FH , "<" , "README.pod" ) ; {
    local $/=" " ;      # enable "slurp" mode, altera o padrào ed "\n" para espaço.
    while ( <FH> ) {
        warn $_;
    }
};

Modos de abrir para leitura, escrita e append

open FH, "<arquivo.txt"  # abre para leitura
open FH, ">arquivo.txt"  # abre para escrita. irá substituir o conteúdo
open FH, ">>arquivo.txt" # abre para append, cola o texto ao final do arquivo

Outra opção é usar o formato de 3 argumentos do comando open:

open FH, "<",  "arquivo.txt" # abre para leitura
open FH, ">",  "arquivo.txt" # abre para escrita. irá substituir o conteúdo
open FH, ">>", "arquivo.txt" # abre para append, cola o texto ao final do arquivo

O comando open tambem aceita $ na variável, ex

open $FH, "<",  "arquivo.txt" # abre para leitura

Para escrever no arquivo, é possível executar o comando print junto com o filehandle associado ao arquivo, ex:

print FH "Novo texto para o arquivo...";
print FH "Mais texto", "\ne mais e mais", "\ne mais e mais...";

File::Slurp

Este módulo é fantastico para trabalhar com arquivos. Além deste módulo ser muito bem recomendado, tem padrões excelentes para abrir e escrever arquivos.

use File::Slurp;
my $text = read_file( "texto.txt" );
my @linas = read_file( "texto.txt" );

Veja as demais opções na documentação:

$ perldoc File::Slurp

Com Typeglob e filehandles

perldoc -f perldata para mais informações

sub newopen {
    my $path = shift;
    local *FH; # not my!
    open (FH, $path) or return undef;
    return *FH;
}
$fh = newopen('/etc/passwd');
while ( <$fh> ) { warn $_ }

Statements if, else

Em perl é possível usar o if de maneira tradicional:

if ( $variavel == $valor ) {
    ...
}

No entanto tambem é possível usar o if ao final das expressões, ex:

sub { warn 1; warn 2; warn 3 }->() if ( 1 == 1 );
warn "1 é igual a 1" if ( 1 == 1 );
warn "1 é igual a 1" if 1 == 1;

if, elsif else:

if ( 1 == 2 ) {
    warn "if"
} elsif ( 2 == 2 ) {
    warn "elsif"
} else { 
    warn "else" 
}

Construção de classes / objetos

$ perldoc perlootut
$ perldoc perlobj

Em perl um objeto é apenas uma estrutura de dados que sabe a qual classe ela pertence. Uma classe é simplesmente um pacote/package. A classe disponibiliza métodos que operam nos objetos. Um método é uma subrotina que aceita a referência de um objeto como primeiro argumento, o $self.

No fundo os objetos são um hash que tem uma 'classe' identificadora diferente do tradicional HASH. Ao invés de HASH, vai aparecer Nome::Modulo.

Todas as classes em perl precisam ser finalizadas com um "1;" no final delas, Segue um exemplo simples de um módulo:

package Minha::Classe;    #nome da minha classe
use strict;               
use warnings;

# metodo pular
sub pular {
    my ( $self ) = @_; 
    warn "pular";
}

1;  #última linha da classe

Este 1 é o último comando executado ao fazer uso com 'use' ou 'require'. Ou seja, o interpretador evalua o código e a última linha é o 1, que retorna verdadeiro, indicando que tudo que veio antes foi evaluado corretamente. Caso o 1 não seja retornado, o interpretador entende que pode ter ocorrido uma falha durante a leitura desse módulo consequentemente resultando em erro.

Class::Accessor

$ perldoc Class::Accessor

O Class::Accessor é um excelente módulo para criar classes. Dada uma lista de atributos (passada pra mk_accessors) ele cria todos os accessors da sua classe. Isso facilida bastante pois não há necessidade de criar getters e setters. Consequentemente sobra mais tempo para se preocupar com os atributos em si.

Este módulo era muito utilizado antes do lançamento do Moose e do Moo e Mouse.

package Foo;
use base qw|Class::Accessor|;
Foo->mk_accessors( qw| nome idade | );

use  DDP;
my $f = Foo->new();
$f->idade( 'dimenor' );     #método para setar o valor do atributo idade.
warn $f->idade;             #retorna o valor atual do atributo
warn $f->{_idade};          #variável interna onde o class accessor guarda os valores
warn p $f;

Moose

O Moose extende o sistema padrão de objetos do perl. Ao mesmo tempo o Moose junta as melhores e mais atuais boas práticas utilizadas nas mais diversas linguagens de programação. O foco principal do moose é permitir que programadores foquem mais no que precisam fazer e menos na mecânica de relacionamento a objetos. Um exemplo deste tipo de facilidade, são os getters e setters.. normalmente é necessário criar os getters e setters manualmente e isso é repetitivo e tedioso por ser manual, então o Moose permite que seja declarado apenas os atributos e os getters e setters são criados automaticamente. Nesse exemplo o foco se resume à criação de atributos.

O Moose pode ser bem complexo, mas o basicão dele é simples de entender. Além de definir os atributos e métodos, tambem oferece facilidades não encontradas nos sistemas de orientação a objetos encontrados por ai.. por exemplo: suporte a Roles, Traits, modificadores de métodos (before, around, after)

Exemplo de classe com Moose:

primeiro vou criar uma classe pessoa. Toda pessoa tem um nome, e uma idade. E são atributos que guardam valores relacionados à pessoa. Toda pessoa pode falar, então há um método 'falar' na classe pessoa.

package Pessoa;
use Moose;

has nome  => ( is => 'rw' );
has idade => ( is => 'rw' );

sub falar {
    my ( $self ) = @_;
    warn "falando.."; 
}

1;

agora vou criar uma "Role" chamada Atleta. Role quer dizer papel, ou seja, é como se fosse um papel a mais que eu posso aplicar em cima de uma outra classe. Por exemplo, se tenho uma classe Pessoa , poderia criar uma Classe Atletas que contempla o conhecimento necessário para atletas. Posteriormente poderia criar uma classe que herda de Pessoa e aplica a Role Atleta, gerando assim uma nova classe de PessoaAtleta, veja o exemplo:

package Atleta;
use Moose::Role;

sub correr {
    my ( $self ) = @_; 
    warn qq{meu nome é $self->{nome} e vou correr};
}

1;

e agora vou criar uma classe PessoaAtleta que extende a classe Pessoa. E aplica a role 'Atleta'.

package PessoaAtleta;
use Moose;
extends qw|Pessoa|;
with qw|Atleta|;

1;

E este é o uso da minha classe PessoaAtleta:

use DDP;
my $f = PessoaAtleta->new();
$f->nome('Jão');
$f->idade( 'dimenor' );     #método para setar o valor do atributo idade.
warn $f->idade;             #retorna o valor atual do atributo
warn $f->correr;
warn p $f;

Outra facilidade do moose, é poder passar os atributos a serem populados já no método new. Tambem é possível passar um objeto hash.

my $p2 = PessoaAtleta->new(
    nome    => 'Jão',
    idade   => 'dimenor'
);
warn $p2->correr;
warn $p2->nome;
warn p $p2;

Moo

O Moo veio após o Moose principalmente pois algumas pessoas queriam melhorar o desempenho do Moose, no entanto algumas funcionalidades essenciais do moose (dependendo do uso) influenciam no tempo de inicialização do objeto. Para a maioria dos projetos, esse tempo é irrelevante, mas em projetos onde microsegundos fazem a diferença entre lucro ou prejuízo pressionou para a criação de algo similar ao Moose mas sem todas as funcionalidades e que fosse muito mais rápido.

O Moo tem o básico do Moose e é compatível com Moose. Então é possível criar uma classe Pessoa, Atleta e PessoaAtleta da segunte maneira com Moo:

package Pessoa;
use Moo;

has nome  => ( is => 'rw' );
has idade => ( is => 'rw' );

sub falar {
    my ( $self ) = @_;
    warn "falando.."; 
}

1;

package Atleta;
use Moo::Role;

sub correr {
    my ( $self ) = @_; 
    warn qq{meu nome é $self->{nome} e vou correr};
}

1;

package PessoaAtleta;
use Moo;
extends qw|Pessoa|;
with qw|Atleta|;

1;

use DDP;
my $f = PessoaAtleta->new();
$f->nome('Jão');
$f->idade( 'dimenor' );     #método para setar o valor do atributo idade.
warn $f->idade;             #retorna o valor atual do atributo
warn $f->correr;
warn p $f;

my $p2 = PessoaAtleta->new(
    nome    => 'Jão',
    idade   => 'dimenor'
);
warn $p2->correr;
warn $p2->nome;
warn p $p2;

Bless

use strict;
use warnings;

package Algo;

sub new {
    my $self  = shift;
    my $class = ref($self) || $self;
    return bless {}, $class;
}

sub alo {
    my ( $self );
    warn "ALOOOOOOOO\n";
}

sub set_palavra {
    my ( $self ) = @_;
    $self->{palavra} = "LLLLL";
}

sub get_palavra {
    my ( $self ) = @_;
    $self->{palavra}||"";
}

my $x = Algo->new();
$x->alo();
warn $x->isa( "Algo" );
$x->set_palavra();
warn $x->get_palavra();

Map e Grep

map e grep são dois comandinhos que rodam em cima de uma lista. Tambem podem retornar uma lista com o resultado das operações. Usados em conjunto podem ser bastante poderosos para resolver problemas comuns.

map

A função map funciona como um foreach. É como se fosse um foreach $_ ( @items ) { ...faz algo com $_ e joga na lista de retorno, que pode ser usada opcionalmente ... } .

O map trabalha em cima de cada item de uma lista. Isto é, pode fazer qualquer coisa com cada item de uma lista e pode retornar uma lista com o resultado de execução sob cada item da lista de entrada.

my @lista = ( qw| 4 2 5 6 2 3| );
my @lista_times_2 = map {
    $_ * 2
} @lista;
use DDP; warn p @lista_times_2;

não precisa necessariamente retornar uma lista, o map pode ser usado e seu retorno não precisa ser atribuído a nada, por exemplo:

my @lista = ( qw| 4 2 5 6 2 3| );
my @outra_lista = ();
map {
    push @outra_lista, $_ if $_ > 3   # se o item for maior que 3 vai inserir em @outra_lista
} @lista;
use DDP; warn p @outra_lista;

grep

O grep funciona como um filtro e retorna os itens que passam o filtro. Ou seja, se eu tenho uma lista e preciso saber quais items atendem a determinados critérios, posso filtrá-los conforme o exemplo:

my @lista = ( qw| 5 21 2 5 6 77 3 3 54 2| );
my @menores_q_6 = grep { $_ < 6 } @lista;
use DDP ; warn p @menores_q_6;

Ou se tivesse uma lista e precisasse filtrar os nomes que terminam em a:

my @nomes = (qw|  joao maria marniana joana jorge renato debora beatriz   |);
my @terminando_em_a = grep { $_ =~ /a$/ } @nomes; #poderia ser assim tb: grep { /a$/ } @nomes;
use DDP; warn p @terminando_em_a;

Tambem é possível trabalhar com os atributos de objetos. Este caso faz grep no atributo nome terminando em a.

my @pessoas = (
    #pessoa 1
    {
        nome => 'joao'
    },
    #pessoa 2
    {
        nome => 'maria',
    },
    #pessoa 3
    {
        nome => 'debora',
    },
);
my @terminando_em_a = grep { $_->{ nome } =~ /a$/ } @pessoas;
use DDP; warn p @terminando_em_a;

Então o que dá pra perceber é que map trabalha em cima de uma lista, e pode retornar uma lista. Grep tambem, trabalha em cima de uma lista e pode retornar uma lista. Isso quer dizer que os comandos são compatíveis para serem usados em conjunto, pois dada uma lista, o map pode ser usado junto com um grep que retorna uma lista e passa para o map executar em seguida. Veja o exemplo, muito bom que foi publicado na internet em http://www.pal-blog.de/entwicklung/perl/perl-map-und.html?utm_source=pal-blog&utm_medium=RSS&utm_campaign=RSS:

Suponha que você tem uma lista de números, e precisa saber quais são menores que dez, e multiplicar esses itens por 2.

A solução mais comum e genérica com encaixe em qualquer linguagem é:

my @a = (10,1,3,42,5,2,666,9);
my @b;
for my $item (@a) { # Alle Elemente von @a durchgehen
   if ($item < 10) { # Nur Werte kleiner als 10 bearbeiten
      push @b, $item * 2; # Wert verdoppeln und an @b anhängen
   }
}

Uma outra possível solução mais voltada à linguagem perl, envolveria o uso de map e grep.

my @a = (10,1,3,42,5,2,666,9); # lista de itens
my @temp = grep { $_ < 10 } @a; # filtra os itens menores que dez e joga em @temp
my @b = map { $_ * 2 } @temp; # pra cada item em temp, executa item * 2 e joga em @b

No comando anterior, grep vem antes de map. E cada um está separado, cada um em sua linha, Eles podem trabalhar em conjunto pois ambos precisam de uma lista como input, e ambos tem uma lista como output. Ou seja, é possivel usar eles em uma única linha, ex:

my @a = (10,1,3,42,5,2,666,9);
my @b = map { $_ * 2 } grep { $_ < 10 } @a;

e outra opção seria executar o filtro sem grep e apenas dentro do map:

my @a = (10,1,3,42,5,2,666,9);
my @b = map { if ($_ < 10) { $_ * 2 } else { () } } @a;

Sort

*TODO sort por coluna

$ perldoc -f sort

ordenação numérica

my @numbers = (qw(2 43 5 6 7 4 2 1 3 5 63  34 2 22 3 4 5));
my @sorted  = sort { $a <=> $b } @numbers;  #sort numérico
use DDP; warn p @sorted;

ordenação alfabética

my @nomes = (qw|maria joao gabriela helena alan tiago zico ricardo rogério|);
my @sorted_nomes = sort @nomes;
use DDP; warn p @sorted_nomes;

ordenação por atributo

my @pessoas = (
    {
        nome => 'maria',
    },
    {
        nome => 'joao',
    },
    {
        nome => 'gabriela',
    },
    {
        nome => 'helena',
    },
);
my @sorted_pessoas = sort { $a->{ nome } cmp $b->{ nome } } @pessoas;
use DDP; warn p @sorted_pessoas;

ordenação de chaves de um hash

my $hash = {
    c => '',
    b => '',
    a => ''
};
#sort
my @sorted_keys_hash = sort keys $hash;
warn p @sorted_keys_hash;
#reverse sort
my @reverse_sorted_keys_hash = reverse sort keys $hash;
warn p @reverse_sorted_keys_hash;

ordenação por chaves de uma lista ordenada

my %form = (
    cc => '',
    bb => '',
    aa => '',
);
my @sorted_keys_form = sort keys %form;
warn p @sorted_keys_form;

Ordenação por valores de um hash

#ordenando por valor:
my %form = (
    cc => 'aa',
    bb => 'bb',
    aa => 'cc',
);
my @sorted_values_form = sort { $form{$a} cmp $form{$b} } keys %form;
warn p @sorted_values_form;

Itens vazios na lista de retorno

As vezes é preciso jogar vazio na lista de retorno. A primeira intenção é jogar um undefined. Mas como é uma lista, a maneira correta é atribuir um item de lista vazia, que resulta em nada.

my @lista               = ( qw| joao amanda leticia jessica jenifer jorge roberto | );
my @terminando_em_a     = map {  if ( $_ =~ m!a$! ) { $_ } else { () } } @lista;
warn p @terminando_em_a;

Itens que aparecem em duas listas

O map e grep tambem podem ajudar quando é necessário saber quais items estão presentes em duas listas.

use DDP; 
use strict; 
use warnings; 
my @list    = (qw|1 2 3 4 5|); 
my @list2   = (qw| 2 3 10 20 30 40 6 7 8|); 
my $items   = {map { $_ => 1 } @list }; 
my @on_both = grep { exists $items->{ $_ } } @list2; 
warn p @on_both;

Itens presentes nas duas listas:

#saída
#[
#    [0] 2,
#    [1] 3
#] at t.pl line 16.

Uma outra opção igual à acima jogar o map direto ao invés de declarar $items, ficando da seguinte maneira:

use DDP; 
use strict; 
use warnings; 
my @list        = (qw|1 2 3 4 5|); 
my @list2       = (qw| 2 3 10 20 30 40 6 7 8|); 
my @on_both     = grep { exists {map { $_ => 1 } @list }->{ $_ } } @list2; 
warn p @on_both;

Testes

Por padrão todos os módulos perl vêm com testes. Os testes se encontram no diretório ./t/ dentro do diretório do módulo. Os testes nada mais são que automatizações que tentam executar a aplicação com objetivo de provar que a app/módulo está funcionando. O mesmo teste pode ser executado em outra versão de perl ou outro tipo de máquina e tem que funcionar em todas por igual.

A suíte/framework de testes mais famosa é o Test::More pois ela é completa e tambem é simples de usar.

$ perldoc Test::More

Test::More

use Test::More tests => 23;     # exige que seja executado exatamente 23 testes
use Test::More;                 # executa testes até encontrar comando: "done_testing()"

ok  ($got eq $expected, $test_name);  # um teste
is  ($got, $expected, $test_name);    # outro teste
isnt($got, $expected, $test_name);    # outro teste

done_testing(); #indica que chegou ao fim para printar stats

Conexão com banco de dados

Tutorial avançado de DBI by Tim Bunce: http://www.slideshare.net/Tim.Bunce/dbi-advanced-tutorial-2007

Para trabalhar com banco de dados é necessário instalar o módulo do banco de dados que será utilizado

DBI

$ perldoc DBI

http://dbi.perl.org/

O DBI é a interface padrão de bancos de dados. O DBI fornece métodos, variáveis e convenções para manter a interface de dados consistente e independente do banco de dados em uso. Em outras palavras, o mesmo código tem que rodar em qualquer banco de dados sem precisar mexer no código. Isto quer dizer, caso decida testar outros bancos ou mudar o banco não será necessário alterar nenhuma linha de código. Seriam alterados apenas os dados de conexão e autenticação da conexão.

DBIx::Class

dbicdump, com ele é possível automatizar a geração de todos os models a partir de uma conexão com o banco de dados. Ou seja, o dbicdump vai conectar no banco especificado, listar as tabelas e colunas. Se o banco de dados tiver suporte ele consegue detectar os relacionamentos. No final será gerado um diretório com o as classes do seu módulo ORM para conexão com esse DB.

Feito isso já é possível usar esse módulo para conectar no banco de dados etc.

dbicdump -o dump_directory=./lib \
-o components='["InflateColumn::DateTime"]' \   <-- *** nao obrigatorio
-o debug=1 \
My::Schema \
'dbi:Pg:dbname=foo' \
 myuser \
mypassword

Quais relacionamentos são detectados automaticamente ? has one, has many, belongs to. Many to many não é detectado automaticamente e tem que ser adicionado manualmente.

Fey::ORM

Referências

perldoc
perl.org

APÊNDICE

TEMA LIVRE

Esta seção é destinada a pessoas interessadas em colaborar com soluções pontuais que sirvam como dicas e referência para outros usuários. Deixo alguns exemplos de soluções pontuais que me interessaria conhecer.

Trabalhando com formulários ascii

Esta dica é ótima para pessoas que precisam imprimir boletos, ou fomulários ascii, por exemplo:

,=============================================================.
| COD. | NOME COMPLETO                            | STATUS    |
| 44   | Jao da Silva                             | casado    |
| 23   | Jorge Pereira                            | solteiro  |
| 32   | David Ramos                              | casado    |

Existe um módulo específico para isso. O Perl6::Form. Com ele é possível criar qualquer tipo de formulário. O módulo alinha o texto de acordo com o layout escolhido.

O exemplo a seguir mostra como printar uma lista de valores em 3 colunas usando listas e '[' como opção de layout que quer dizer 'preencha todas as linhas a seguir com dados de uma lista'

use strict;
use warnings;
use Perl6::Form;

my @cods = (qw|44 23 32|);
my @nomes = ('Jao da Silva', 'Jorge Pereira', 'David Ramos' );
my @status = (qw|casado solteiro casado|);

my $text = form      ",=============================================================.",
                     "| COD. | NOME COMPLETO                            | STATUS    |",
                     "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
                     "| {[[} | {[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[} | {[[[[[[[} |",
                        \@cods , \@nomes,                                 \@status;

warn $text;

Benchmarking

Good example: http://www.pal-blog.de/2014/04/18/sub_arguments_benchmark.zip http://blogs.perl.org/users/leon_timmermans/2013/05/why-you-dont-need-fileslurp.html

manipulando arquivos excel com perl

#TODO..

acessar páginas de internet com perl

#TODO..

AUTHOR

Hernan Lopes

OUTROS CONTRIBUIDORES

Se você ajudou a melhorar este documento, insira seu nome abaixo:

About

Programando perl moderno - para iniciantes

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages