DevTools

Cheatsheet Perl

Linguagem de scripting poderosa para texto, sysadmin e automação

Voltar às linguagens
Perl
112 cards encontrados
Categorias:
Versões:

Básico e Variáveis


12 cards
Estrutura do programa
#!/usr/bin/env perl
use strict;
use warnings;
use v5.36;

# O meu primeiro script
say "Olá Mundo!";

my $nome = "Ana";
say "Olá, $nome!";

Um script Perl começa com o shebang #!/usr/bin/env perl. Os pragmas use strict e use warnings são obrigatórios em código moderno. say imprime com nova linha automática.

Strings e operadores
my $s = "Olá" . " Mundo";   # concatenação
my $rep = "ab" x 3;          # "ababab"
my $len = length($s);        # 11

uc("texto");      # "TEXTO"
lc("TEXTO");      # "texto"
ucfirst("nome");  # "Nome"
substr($s, 0, 3); # "Olá"
index($s, "Mundo"); # posição

O operador . concatena strings e x repete. Funções como uc, lc, substr e index manipulam texto. length devolve o tamanho. Aspas duplas interpolam variáveis; aspas simples são literais.

Operadores especiais (// e or)
# Defined-or (//):
my $val = $input // "default";
# usa $input se definido (mesmo 0 ou "")

# Or de baixa precedência:
open(my $fh, "<", $file) or die "Erro: $!";

# Ternário:
my $status = $idade >= 18 ? "adulto" : "menor";

# Auto-incremento em string:
my $col = "A";
$col++;   # "B", ..., "Z", "AA"...

O operador // (defined-or) devolve o operando direito só se o esquerdo for undef — ao contrário de || que também trata 0 e "" como falsos. or die é o padrão idiomático de tratamento de erro.

Variáveis escalares ($)
my $nome = "Ana";
my $idade = 30;
my $pi = 3.14159;
my $ativo = 1;       # verdadeiro
my $vazio = undef;   # sem valor

# Interpolação em aspas duplas:
print "Olá, $nome! Tens $idade anos.\n";

# Concatenação com ponto:
my $msg = $nome . " tem " . $idade;

Escalares ($) guardam um único valor: string, número ou undef. Perl converte automaticamente conforme o operador — . concatena, + soma. my declara variáveis léxicas com âmbito limitado ao bloco.

qw e here-doc
# qw (quote words) - lista de palavras:
my @cores = qw(vermelho verde azul);
# igual a ("vermelho", "verde", "azul")

# Here-doc (string multi-linha):
my $texto = <<FIM;
Linha 1
Linha 2 com $nome
FIM

# sprintf (formatação):
my $fmt = sprintf("%.2f", 3.14159);  # "3.14"

qw() cria listas de palavras sem aspas nem vírgulas. O here-doc <<FIM permite strings multi-linha com interpolação. sprintf formata números e strings com controlo total de precisão.

Pragmas
use strict;     # exige declaração de variáveis
use warnings;   # avisa sobre problemas
use v5.36;      # versão moderna (inclui ambos)
use utf8;       # código-fonte em UTF-8
use feature 'say';  # say = print + \n

# Com use v5.36+:
use v5.36;
say "Olá sem \n manual";

# Desativar temporariamente:
no warnings 'numeric';
my $n = "12abc" + 0;   # sem warning

use strict impede variáveis não declaradas (typos viram erros). use warnings alerta sobre operações suspeitas. use v5.36 ativa ambos mais funcionalidades modernas como say. Estes pragmas transformam bugs silenciosos em erros detetáveis.

Arrays (@)
my @frutas = ("maçã", "banana", "uva");
my @nums = (1..10);   # range 1 a 10

# Acesso (índice começa em 0):
my $primeiro = $frutas[0];   # "maçã"
my $ultimo = $frutas[-1];    # "uva"

# Tamanho:
my $tamanho = scalar @frutas;   # 3

Arrays (@) são listas ordenadas com índice a começar em 0. O sigilo muda: @frutas para a lista, $frutas[0] para um elemento. Índices negativos contam do fim (-1 é o último).

Contexto
my @lista = (1, 2, 3);

# Contexto escalar vs lista:
my $tamanho = @lista;   # 3 (escalar)
my @copia = @lista;     # (1,2,3) (lista)

# Atribuição em lista:
my ($a, $b, $c) = (10, 20, 30);
my ($nome, $idade) = split(/,/, "Ana,30");

O conceito de contexto é central em Perl: a mesma expressão comporta-se de forma diferente em contexto escalar vs lista. Um array em contexto escalar devolve o tamanho; em contexto de lista devolve os elementos.

undef e defined
my $x = undef;   # sem valor

# Verificar se está definido:
if (defined $x) {
    say "Tem valor";
}

# Definir com defined-or:
$x //= 42;   # só se $x for undef

# undef como função:
undef $x;    # limpa a variável
my @arr = ();
undef @arr;  # esvazia o array

undef representa a ausência de valor (como null). defined verifica se uma variável tem valor. O operador //= atribui só se for undef. Não confunda undef com 0 ou string vazia — são valores definidos.

Hashes (%)
my %pessoa = (
    nome   => "Ana",
    idade  => 30,
    cidade => "Lisboa",
);

# Acesso:
my $nome = $pessoa{nome};
$pessoa{email} = "ana@mail.com";

# Chaves e valores:
my @chaves = keys %pessoa;
my @valores = values %pessoa;

Hashes (%) são dicionários chave/valor. A sintaxe chave => valor é açúcar para ("chave", "valor"). keys devolve as chaves e values os valores. O acesso faz-se com chavetas: $pessoa{nome}.

Operador diamante e chomp
# Ler linhas de ficheiros/STDIN:
while (my $linha = <>) {
    chomp $linha;   # remove \n final
    print $linha;
}

# chomp vs chop:
my $s = "Olá\n";
chomp $s;   # remove só o \n final
chop $s;    # remove o último carácter

O operador diamante <> lê linhas dos ficheiros passados como argumentos ou do STDIN. chomp remove a nova linha final (essencial ao ler input). chop remove o último carácter, seja qual for.

say vs print
# print (sem nova linha automática):
print "Olá";
print "Mundo\n";   # \n manual

# say (com nova linha, Perl 5.10+):
say "Olá";    # imprime "Olá\n"
say "Mundo";

# print com filehandle:
print $fh "linha\n";

# say para stderr:
say STDERR "Erro!";

say é como print mas adiciona nova linha automaticamente (requer use feature 'say' ou use v5.36). print é mais flexível para output sem quebra de linha. Ambos aceitam um filehandle como primeiro argumento.

Subrotinas e Funções


12 cards
Subrotina básica
# Definição:
sub saudacao {
    my ($nome, $hora) = @_;
    return "Olá, $nome! ($hora)";
}

# Chamada:
my $msg = saudacao("Ana", "manhã");
say $msg;

# Sem parênteses (idiomático):
saudacao "Ana", "tarde";

Subrotinas definem-se com sub. Os argumentos chegam via @_ e o padrão my ($a, $b) = @_ faz destructuring. return devolve o valor (a última expressão é o retorno implícito).

Closures
sub criar_contador {
    my $count = 0;   # variável léxica privada
    return sub {
        $count++;
        return $count;
    };
}

my $c = criar_contador();
say $c->();   # 1
say $c->();   # 2 (count persiste!)

# Cada contador é independente:
my $d = criar_contador();
say $d->();   # 1

Closures capturam variáveis léxicas do âmbito onde foram criadas — $count "vive" dentro da sub anónima mesmo após criar_contador() retornar. Cada chamada produz um contador independente.

sort
my @nums = (3, 1, 4, 1, 5);

# Numérico crescente:
my @asc = sort { $a <=> $b } @nums;

# Numérico decrescente:
my @desc = sort { $b <=> $a } @nums;

# Alfabético:
my @alpha = sort { $a cmp $b } @strings;

# Por campo de hash:
my @por_idade = sort {
    $a->{idade} <=> $b->{idade}
} @pessoas;

sort ordena com um comparador custom. $a e $b são os dois elementos comparados; o bloco devolve negativo, zero ou positivo. Use <=> para números e cmp para strings.

@_ e parâmetros
# @_ contém todos os argumentos:
sub demo {
    say "Total: " . scalar @_;
    say "Primeiro: $_[0]";
    say "Último: $_[-1]";
}

# Destructuring com defaults:
sub conectar {
    my ($host, $porta) = @_;
    $porta //= 3306;   # valor por defeito
}

@_ é o array especial com os argumentos passados. $_[0] é o primeiro, $_[-1] o último. O operador //= define valores por defeito para parâmetros opcionais.

state
use feature 'state';

sub contador {
    state $count = 0;   # persiste entre chamadas
    return ++$count;
}

say contador();   # 1
say contador();   # 2
say contador();   # 3

state (Perl 5.10+) cria variáveis que persistem entre chamadas sem serem globais. A inicialização acontece só na primeira chamada. É ideal para contadores e caches privados.

reduce e List::Util
use List::Util qw(reduce sum max min first);

my @nums = (3, 1, 4, 1, 5, 9);

my $soma = sum @nums;       # 23
my $maior = max @nums;      # 9
my $menor = min @nums;      # 1

# reduce (acumulador genérico):
my $total = reduce { $a + $b } @nums;

# first (primeiro que satisfaz):
my $x = first { $_ > 4 } @nums;   # 5

List::Util (core) elimina loops manuais: sum, max, min fazem agregações; first devolve o primeiro elemento que satisfaz o teste; reduce é o acumulador genérico para qualquer operação binária.

Hash de opções (named params)
sub criar_user {
    my %opts = @_;
    my $nome  = $opts{nome} // die "nome obrigatório";
    my $email = $opts{email};
    my $admin = $opts{admin} // 0;
    say "$nome ($email)";
}

# Chamada auto-documentada:
criar_user(nome => "Ana", admin => 1);

O padrão de hash de opções (%opts = @_) simula parâmetros nomeados: a chamada fica auto-documentada (nome => "Ana") e a ordem não importa. É o padrão preferido para funções com muitos parâmetros.

map
# Transformar cada elemento:
my @dobros = map { $_ * 2 } (1..5);
# (2, 4, 6, 8, 10)

# Extrair campo de hashes:
my @nomes = map { $_->{nome} } @pessoas;

# Construir hash:
my %por_id = map { $_->{id} => $_ } @registos;

# $_ é o elemento atual

map transforma cada elemento de uma lista, podendo produzir 0, 1 ou N resultados por input. $_ é o elemento atual. É ideal para extrair campos de hashes ou construir novas estruturas.

Recursão
sub fatorial {
    my ($n) = @_;
    return 1 if $n <= 1;
    return $n * fatorial($n - 1);
}

say fatorial(5);   # 120

sub fibonacci {
    my ($n) = @_;
    return $n if $n < 2;
    return fibonacci($n-1) + fibonacci($n-2);
}

Perl suporta recursão naturalmente. Toda função recursiva precisa de um caso base (return 1 if $n <= 1) para parar. Para recursão profunda, prefira iteração — Perl não otimiza tail-calls.

Sub anónima e code refs
# Sub anónima:
my $dobro = sub { $_[0] * 2 };
say $dobro->(21);   # 42

# Code ref como parâmetro:
sub aplicar {
    my ($fn, $valor) = @_;
    return $fn->($valor);
}
say aplicar(sub { $_[0] + 1 }, 41);   # 42

# Referência a sub nomeada:
my $ref = \&saudacao;
$ref->("Ana", "noite");

Subs anónimas (sub { }) são valores de primeira classe, guardadas em escalares e passadas como argumentos. Chamam-se com a seta ->(). \&nome cria uma referência a uma sub nomeada.

grep
# Filtrar elementos:
my @maiores = grep { $_ > 18 } @idades;

my @ativos = grep { $_->{ativo} } @users;

# Em contexto booleano (verificar existência):
if (grep { $_ > 100 } @valores) {
    say "Há valores grandes";
}

# $_ é o elemento testado

grep filtra uma lista, devolvendo só os elementos que passam no teste (o bloco devolve verdadeiro). $_ é o elemento testado. Em contexto booleano verifica se algum elemento satisfaz a condição.

Memoização
use Memoize;
memoize('fibonacci');

sub fibonacci {
    my ($n) = @_;
    return $n if $n < 2;
    return fibonacci($n-1) + fibonacci($n-2);
}

# Memoização manual:
my %cache;
sub fib {
    my ($n) = @_;
    return $cache{$n} //= ($n < 2 ? $n
        : fib($n-1) + fib($n-2));
}

Memoização guarda em cache os resultados de funções puras — fibonacci(40) passa de minutos para instantâneo. O módulo Memoize faz isto automaticamente; a versão manual usa um hash com //=.

Avançado


12 cards
eval (try/catch nativo)
# eval captura die:
my $result = eval {
    die "Algo falhou!\n" if $erro;
    calcular();
};

if ($@) {
    warn "Erro capturado: $@";
}

# $@ contém a mensagem do die
# eval retorna undef se houver erro

Perl usa eval { } + die como mecanismo de exceções nativo. eval captura o die mais próximo e guarda a mensagem em $@. Se não houver erro, $@ fica vazio. É a base do tratamento de exceções em Perl.

CPAN e cpanm
# Instalar módulos:
# cpanm Module::Name      (App::cpanminus)
# cpan Module::Name       (cliente clássico)

# Módulos essenciais (core):
use Getopt::Long;    # args de linha de comandos
use File::Basename;  # basename/dirname
use POSIX qw(strftime ceil);
use Time::Piece;     # datas

# CPAN populares:
use DBI;             # bases de dados
use LWP::UserAgent;  # HTTP
use Mojolicious;     # web framework

CPAN tem 200.000+ módulos; cpanm instala-os facilmente. Módulos core vêm com o Perl (Getopt::Long, POSIX, Time::Piece). DBI é a interface universal de bases de dados; LWP faz pedidos HTTP; Mojolicious é um framework web.

Tied variables
# Tie hash a ficheiro (persistência):
use DB_File;
tie my %db, 'DB_File', "dados.db"
    or die "Não abri: $!";
$db{chave} = "valor";   # persiste!
untie %db;

# Tie::File (ficheiro como array):
use Tie::File;
tie my @linhas, 'Tie::File', "f.txt";
$linhas[0] = "Nova primeira linha";
untie @linhas;

tie associa uma variável a uma classe que interceta todas as operações — a variável parece normal mas o comportamento é custom. DB_File persiste hashes em disco; Tie::File trata um ficheiro como array (cada linha é um elemento).

Try::Tiny
use Try::Tiny;

try {
    operacao_perigosa();
} catch {
    warn "Erro: $_";   # $_ tem a mensagem
} finally {
    cleanup();
};

# Mais seguro que eval/$@:
# resolve problemas subtis de localização de $@

Try::Tiny oferece sintaxe try/catch/finally limpa e resolve problemas subtis do eval/$@ (localização, objetos). A mensagem do erro fica em $_ dentro do catch. É a forma recomendada de tratar exceções em Perl moderno.

Getopt::Long
use Getopt::Long;

my $verbose = 0;
my $output  = "out.txt";
my @includes;

GetOptions(
    "verbose|v"   => \$verbose,
    "output|o=s"  => \$output,
    "include|I=s" => \@includes,
) or die "Uso: $0 [-v] [-o ficheiro]\n";

# perl script.pl -v -o res.txt -I a -I b
say "Output: $output" if $verbose;

Getopt::Long parseia argumentos de linha de comandos: =s espera string, |v cria alias curto. Preenche variáveis por referência. É o padrão para scripts com opções (--verbose, --output=ficheiro).

Data::Dumper
use Data::Dumper;

my $dados = {
    users => [ { nome => "Ana" }, { nome => "Rui" } ],
    total => 2,
};

# Inspeção para debugging:
print Dumper($dados);

# Configuração útil:
$Data::Dumper::Sortkeys = 1;   # ordenar chaves
$Data::Dumper::Indent = 1;     # indentação simples

Data::Dumper imprime a estrutura completa de qualquer referência em formato Perl legível — indispensável para debugging de estruturas complexas. Sortkeys ordena as chaves para output consistente.

Carp
use Carp qw(croak carp confess cluck);

# croak = die na linha do CHAMADOR:
sub dividir {
    my ($a, $b) = @_;
    croak "Divisão por zero" if $b == 0;
}

# carp = warn na linha do chamador:
carp "Valor suspeito";

# confess = die com stack trace completo:
confess "Erro grave";

Carp melhora as mensagens de erro em módulos: croak reporta o erro na linha do chamador (não do módulo), carp faz o mesmo para avisos, confess inclui o stack trace completo. Essencial para código de produção.

fork (processos)
my $pid = fork();

if ($pid == 0) {
    # processo filho
    trabalho_pesado();
    exit 0;
} elsif ($pid > 0) {
    # processo pai
    waitpid($pid, 0);   # espera o filho
    say "Filho terminou";
} else {
    die "fork falhou: $!";
}

fork() cria um processo filho com memória independente (sem race conditions). Retorna 0 no filho e o PID no pai. waitpid espera o filho terminar. É o modelo de concorrência mais natural e seguro em Perl.

JSON
use JSON;

my $dados = { nome => "Ana", idade => 30 };

# Serializar:
my $json = encode_json($dados);
# {"nome":"Ana","idade":30}

# Desserializar:
my $obj = decode_json($json);
say $obj->{nome};   # Ana

# Pretty print:
my $bonito = JSON->new->pretty->encode($dados);

O módulo JSON converte entre estruturas Perl e JSON: encode_json serializa, decode_json desserializa. Hashes viram objetos JSON e arrays viram listas. pretty formata com indentação para leitura.

use vs require
# use (compile-time + import):
use JSON;
use List::Util qw(sum max);

# require (runtime, sem import):
require JSON;
JSON->import;   # import manual

# require condicional:
if ($debug) {
    require Data::Dumper;
    Data::Dumper->import;
}

# use é avaliado antes de executar

use carrega e importa em tempo de compilação (erros param a compilação); require carrega em tempo de execução (para módulos opcionais). use Modulo qw(...) é equivalente a require + import. Use require para dependências condicionais.

Parallel::ForkManager
use Parallel::ForkManager;

my $pm = Parallel::ForkManager->new(4);   # 4 workers

for my $url (@urls) {
    $pm->start and next;   # fork (pai salta)
    download($url);        # só no filho
    $pm->finish;           # exit do filho
}

$pm->wait_all_children;
say "Todos terminados";

Parallel::ForkManager simplifica pools de workers com limite de processos. $pm->start faz fork (o pai salta com and next); o filho executa e chama $pm->finish. Ideal para processar muitas tarefas em paralelo.

DateTime
use DateTime;

my $dt = DateTime->now;
say $dt->ymd;        # 2024-01-15
say $dt->hms;        # 14:30:00
say $dt->year;

# Aritmética:
$dt->add(days => 7);
my $futuro = $dt->clone->add(months => 1);

# Diferença:
my $d = DateTime->new(year => 2024, month => 3, day => 1);
my $diff = $d->delta_days($dt);

DateTime (CPAN) é a forma robusta de trabalhar com datas: ymd/hms formatam, add soma tempo, clone evita mutação. delta_days calcula diferenças. Muito superior a localtime para cálculos de datas.

Arrays e Hashes


12 cards
push / pop / shift / unshift
my @fila = (1, 2, 3);

push @fila, 4;       # adiciona no fim → (1,2,3,4)
pop @fila;           # remove do fim → (1,2,3)
unshift @fila, 0;    # adiciona no início → (0,1,2,3)
shift @fila;         # remove do início → (1,2,3)

# push múltiplos:
push @fila, (5, 6, 7);

push/pop manipulam o fim do array; unshift/shift manipulam o início. push e unshift aceitam vários elementos. shift é muito usado para retirar o primeiro argumento em subrotinas.

each / keys / values
my %pessoa = (nome => "Ana", idade => 30);

# Iterar par a par:
while (my ($k, $v) = each %pessoa) {
    say "$k: $v";
}

# Só chaves / só valores:
for my $k (keys %pessoa) {
    say "$k => $pessoa{$k}";
}
my @vals = values %pessoa;

# exists / delete:
exists $pessoa{nome};    # True
delete $pessoa{idade};   # remove

each itera par a par (mais eficiente em hashes grandes). keys e values devolvem listas. exists verifica se uma chave existe (mesmo com valor undef); delete remove a entrada.

Merge de hashes
my %a = (x => 1, y => 2);
my %b = (y => 3, z => 4);

# Merge (b tem precedência):
my %merged = (%a, %b);   # x=1, y=3, z=4

# Merge sem sobrescrever:
my %safe = (%b, %a);     # y fica 2

# Adicionar só chaves novas:
for my $k (keys %b) {
    $a{$k} //= $b{$k};
}

Merge de hashes faz-se com (%a, %b) — em chaves duplicadas, o segundo hash ganha. Para não sobrescrever, inverta a ordem ou use //= para preencher só chaves em falta.

splice
my @arr = (1, 2, 5, 6);

# Inserir no meio:
splice @arr, 2, 0, (3, 4);   # (1,2,3,4,5,6)

# Remover elementos:
splice @arr, 1, 2;           # remove 2 elems

# Substituir:
splice @arr, 0, 1, (9, 9);   # troca o primeiro

# splice @arr, offset, length, @reposição

splice é a operação mais versátil de arrays: insere, remove e substitui elementos em qualquer posição. A sintaxe é splice @arr, offset, length, @reposição. Com length 0 só insere; sem reposição só remove.

exists vs defined
my %h = (a => 1, b => undef);

exists $h{a};     # True (chave existe)
exists $h{b};     # True (chave existe, valor undef)
exists $h{c};     # False (chave não existe)

defined $h{a};    # True (valor não-undef)
defined $h{b};    # False (valor é undef)
defined $h{c};    # False

# delete vs undef:
delete $h{b};     # remove a chave
$h{b} = undef;    # chave fica, valor undef

exists verifica se a CHAVE existe; defined verifica se o VALOR não é undef. Uma chave pode existir com valor undef (exists true, defined false). delete remove a chave; atribuir undef mantém-na.

Ordenar hash por valor
my %notas = (Ana => 15, Rui => 18, Mia => 12);

# Chaves ordenadas por valor (desc):
my @top = sort { $notas{$b} <=> $notas{$a} } keys %notas;

for my $nome (@top) {
    say "$nome: $notas{$nome}";
}
# Rui: 18, Ana: 15, Mia: 12

Para ordenar um hash pelo valor, ordenam-se as keys comparando $hash{$b} <=> $hash{$a} (descendente) ou $hash{$a} <=> $hash{$b} (ascendente). O resultado é uma lista de chaves ordenadas.

slice
my @frutas = qw(maçã banana uva pera kiwi);

# Array slice:
my @sub = @frutas[0, 2, 4];   # (maçã, uva, kiwi)

# Hash slice:
my %h = (a => 1, b => 2, c => 3);
my @vals = @h{qw(a c)};       # (1, 3)

# Atribuição via slice:
@h{qw(x y)} = (10, 20);

Slices acedem a vários elementos de uma vez. Note o sigilo: @frutas[0,2] (array slice usa @) vs $frutas[0] (um elemento usa $). Hash slices usam @h{qw(...)}.

Hash de hashes
my %dados;
$dados{Ana}{idade} = 30;
$dados{Ana}{cidade} = "Lisboa";
$dados{Rui}{idade} = 25;

# Acesso:
say $dados{Ana}{cidade};   # Lisboa

# Iterar:
for my $nome (keys %dados) {
    my $info = $dados{$nome};
    say "$nome: $info->{idade} anos";
}

Hashes de hashes são a estrutura mais comum para dados estruturados em Perl. A sintaxe $dados{Ana}{idade} é auto-vivificante: cria os hashes intermédios automaticamente ao atribuir.

List::MoreUtils
use List::MoreUtils qw(uniq any all none mesh);

my @nums = (3, 1, 4, 1, 5, 9, 2, 6);

my @unicos = uniq @nums;   # remove duplicados
any { $_ > 8 } @nums;      # True (algum > 8)
all { $_ > 0 } @nums;      # True (todos > 0)
none { $_ < 0 } @nums;     # True (nenhum < 0)

# mesh (interleave):
my @a = (1, 2, 3);
my @b = qw(a b c);
my @m = mesh @a, @b;   # (1,a,2,b,3,c)

List::MoreUtils (CPAN) estende o List::Util: uniq remove duplicados, any/all/none testam condições, mesh intercala listas. Complementam as funções de alta ordem nativas.

join e split
my @arr = (1, 2, 3, 4);

# join (lista → string):
my $csv = join(",", @arr);     # "1,2,3,4"
my $txt = join(" - ", @arr);   # "1 - 2 - 3 - 4"

# split (string → lista):
my @parts = split(/,/, $csv);       # (1,2,3,4)
my @words = split(/\s+/, $linha);   # por whitespace

# split com limite:
my @top3 = split(/,/, $csv, 3);

join constrói uma string a partir de uma lista com um separador. split faz o inverso: divide uma string em lista por um padrão (regex). São operações inversas e muito usadas para CSV e parsing.

Hash de arrays
my %turmas;
push @{$turmas{A}}, "Ana", "Bruno";
push @{$turmas{B}}, "Carlos";

# Acesso:
say $turmas{A}[0];   # Ana

# Iterar:
for my $turma (keys %turmas) {
    say "Turma $turma:";
    say "  $_" for @{$turmas{$turma}};
}

Hashes de arrays agrupam listas por chave. push @{$turmas{A}} adiciona ao array da chave A (a referência é desreferenciada com @{ }). O acesso combina hash e array: $turmas{A}[0].

Arrays multidimensionais
# Matriz 2D (array de arrays):
my @matriz = (
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
);

# Acesso:
say $matriz[1][2];   # 6

# Percorrer:
for my $linha (@matriz) {
    for my $cel (@$linha) {
        print "$cel ";
    }
    say "";
}

Perl não tem arrays multidimensionais nativos — usam-se arrays de referências a arrays. Cada linha é uma referência [...]. O acesso encadeia índices: $matriz[1][2]. @$linha desreferencia a linha.

Controlo de Fluxo


12 cards
if / elsif / else
my $nota = 15;

if ($nota >= 18) {
    say "Excelente";
} elsif ($nota >= 10) {
    say "Aprovado";
} else {
    say "Reprovado";
}

A estrutura if/elsif/else testa condições em ordem. elsif (não elseif) encadeia múltiplas condições. Os parênteses na condição são opcionais mas recomendados.

while / until
# while (enquanto verdadeiro):
while (my $linha = <$fh>) {
    chomp $linha;
    processar($linha);
}

# until (enquanto falso):
until ($done) {
    $done = tentar();
}

# Loop infinito:
while (1) {
    last if sair();
}

while repete enquanto a condição for verdadeira; until repete enquanto for falsa (o complemento). while (<$fh>) é o padrão clássico de leitura linha-a-linha de um ficheiro.

Comparação numérica vs string
# Numéricos:
==  !=  <  >  <=  >=

# Strings (diferentes!):
eq  ne  lt  gt  le  ge

# O erro clássico:
"10" == "9"   # FALSE (numérico: 10 != 9)
"10" lt "9"   # TRUE  (string: "1" < "9")

# Sempre use o operador certo:
if ($idade >= 18) { }   # numérico
if ($nome eq "Ana") { } # string

Perl separa operadores numéricos (==, <) de operadores de string (eq, lt). Usar o errado é um bug clássico: "10" lt "9" é verdadeiro porque compara lexicograficamente. Use numéricos para números e eq/lt para strings.

unless
my $erro = 0;

# unless = if negado:
unless ($erro) {
    say "Tudo OK";
}

# Equivalente a:
if (!$erro) {
    say "Tudo OK";
}

# Statement modifier:
say "Sem erro" unless $erro;

unless executa o bloco quando a condição é FALSA — mais legível que if (!$cond) para verificações de ausência. Como statement modifier no fim da linha, lê-se naturalmente: "faz isto a menos que...".

do-while
my $input;

# Executa pelo menos uma vez:
do {
    print "Comando: ";
    $input = <STDIN>;
    chomp $input;
} while ($input ne "sair");

say "Adeus!";

O bloco do { } while executa o corpo antes de testar a condição, garantindo pelo menos uma execução. É útil para menus e validação de input do utilizador.

spaceship e cmp
# spaceship (numérico): -1, 0 ou 1
my $r = $a <=> $b;

# cmp (string): -1, 0 ou 1
my $r = $a cmp $b;

# Usados em sort:
my @asc = sort { $a <=> $b } @nums;
my @alpha = sort { $a cmp $b } @strings;

O operador spaceship <=> compara números e cmp compara strings, devolvendo -1, 0 ou 1. São a base dos blocos de ordenação em sort.

Statement modifiers
say "Positivo" if $x > 0;
die "Erro!" unless defined $val;
print "$_\n" for @lista;

# Equivalente a blocos:
if ($x > 0) { say "Positivo" }
for (@lista) { print "$_\n" }

# while modifier:
$total += $_ while @nums;

Statement modifiers (if, unless, for, while no fim da linha) são idiomáticos para ações de uma linha. A leitura fica "ação se condição", natural como inglês. Ideais para código conciso.

next / last / redo
foreach my $n (1..20) {
    next if $n % 2 == 0;   # salta pares
    last if $n > 15;       # sai do loop
    say $n;                # só ímpares até 15
}

# redo (reinicia a iteração):
foreach my $item (@dados) {
    if (!valido($item)) {
        $item = corrigir($item);
        redo;   # repete com $item corrigido
    }
}

next salta para a próxima iteração, last sai do loop completamente, e redo reinicia a iteração atual sem avançar. São o equivalente a continue, break e um "retry" de outras linguagens.

Operador ternário
my $idade = 20;

# condição ? valor_se_true : valor_se_false
my $status = $idade >= 18 ? "adulto" : "menor";
say $status;   # adulto

# Encadeado (evitar!):
my $nivel = $nota >= 18 ? "A"
          : $nota >= 10 ? "B"
          :               "C";

# Melhor com if/elsif para clareza

O operador ternário ?: escolhe entre dois valores numa expressão. É útil para atribuições simples, mas encadear vários ternários reduz a legibilidade — prefira if/elsif nesses casos.

for / foreach
# foreach (idiomático):
foreach my $item (@lista) {
    say $item;
}

# for curto com $_ (alias):
say $_ * 2 for 1..10;

# for estilo C:
for (my $i = 0; $i < 10; $i++) {
    say $i;
}

# for e foreach são sinónimos:
for my $x (@nums) { say $x }

foreach itera sobre listas sem índices; a variável de iteração é um alias do elemento original. for e foreach são sinónimos em Perl. $_ é a variável default quando nenhuma é declarada.

continue
while (<>) {
    next if /^#/;      # salta comentários
    processar($_);
} continue {
    $. = 0 if eof;     # reset do contador de linhas
}

O bloco continue executa após cada iteração do loop, mesmo quando next é usado. Funciona como o incremento do for em C — ideal para manter contadores ou reset de estado entre linhas.

Dispatch table (switch)
# Hash de code refs (mais Perl-ish que switch):
my %acoes = (
    novo    => sub { say "Criar novo" },
    abrir   => sub { say "Abrir ficheiro" },
    guardar => sub { say "Guardar" },
);

my $cmd = "abrir";
if (exists $acoes{$cmd}) {
    $acoes{$cmd}->();   # chama a sub
} else {
    say "Comando desconhecido";
}

Dispatch tables (hash de code refs) são a alternativa idiomática ao switch/case em Perl. Mapeiam valores a ações de forma flexível e extensível. O given/when existe mas é experimental e desaconselhado.

Referências e Estruturas


10 cards
Criar referências
my @arr = (1, 2, 3);
my %hash = (a => 1);
sub funcao { 42 }

my $ref_arr = \@arr;       # ref para array
my $ref_hash = \%hash;     # ref para hash
my $ref_sub = \&funcao;    # ref para sub
my $ref_scalar = \$arr[0]; # ref para scalar

# ref() verifica o tipo:
ref($ref_arr);    # "ARRAY"
ref($ref_hash);   # "HASH"
ref($ref_sub);    # "CODE"

Referências são criadas com barra invertida: \@arr, \%hash, \&sub. São escalares que "apontam" para outra variável. ref() devolve o tipo da referência (ARRAY, HASH, CODE).

Autovivificação
my $dados;   # undef

# Perl cria as estruturas intermédias:
$dados->{users}[0]{nome} = "Ana";

# Agora $dados é:
# { users => [ { nome => "Ana" } ] }

# Cuidado com typos (cria chaves sem erro):
say $dados->{usres};   # undef, sem aviso!

Autovivificação é a criação automática de estruturas intermédias ao aceder — conveniente mas pode mascarar bugs (typos criam chaves em vez de erro). Com use strict e cuidado, é uma funcionalidade poderosa.

Scalar::Util
use Scalar::Util qw(blessed reftype looks_like_number);

blessed($obj);              # classe ou undef
reftype($ref);              # ARRAY, HASH, CODE...
looks_like_number("42");    # True
looks_like_number("abc");   # False

# Verificar antes de desreferenciar:
if (reftype($x) eq 'ARRAY') {
    say "É um array com " . scalar(@$x) . " elems";
}

Scalar::Util fornece introspeção de tipos: blessed devolve a classe de um objeto, reftype o tipo da referência, looks_like_number valida números. Útil para código defensivo antes de desreferenciar.

Referências anónimas
# Array ref anónimo:
my $arr = [1, 2, 3];

# Hash ref anónimo:
my $hash = { nome => "Ana", idade => 30 };

# Code ref anónimo:
my $sub = sub { 42 };

# Aninhados:
my $dados = {
    users => [ { nome => "Ana" }, { nome => "Rui" } ],
};

[] cria uma referência anónima a array e {} a hash (sem precisar de variável nomeada). São a base das estruturas de dados complexas em Perl — equivalentes a arrays e objetos literais de outras linguagens.

Array de hashes
# Lista de registos (padrão muito comum):
my @funcs = (
    { nome => "Ana", dept => "TI", salario => 2500 },
    { nome => "Rui", dept => "RH", salario => 2000 },
);

# Acesso:
say $funcs[0]{nome};   # Ana
$funcs[1]{salario} += 200;

# Iterar:
for my $f (@funcs) {
    say "$f->{nome}: $f->{dept}";
}

Array de hashes é o padrão para listas de registos (como resultados de queries SQL). Cada elemento é uma referência a hash. O acesso combina índice e chave: $funcs[0]{nome}.

Árvores (estrutura recursiva)
# Árvore recursiva:
my $arvore = {
    valor => "root",
    filhos => [
        { valor => "A", filhos => [] },
        { valor => "B", filhos => [
            { valor => "B1", filhos => [] },
        ]},
    ],
};

# Percorrer (recursivo):
sub imprimir {
    my ($no, $nivel) = @_;
    say "  " x $nivel . $no->{valor};
    imprimir($_, $nivel + 1) for @{$no->{filhos}};
}
imprimir($arvore, 0);

Estruturas recursivas como árvores constroem-se com hashes e arrays aninhados. A travessia é naturalmente recursiva. " " x $nivel indenta conforme a profundidade. Data::Dumper ajuda a inspecionar estas estruturas.

Desreferenciar
my $arr = [1, 2, 3];
my $hash = { a => 1, b => 2 };
my $sub = sub { "olá" };

# Array completo:
my @copia = @$arr;

# Elementos:
my $elem = $arr->[0];      # 1
my $val = $hash->{a};      # 1
my $r = $sub->();          # "olá"

# Sem seta (sintaxe antiga):
my $elem2 = $$arr[0];

Desreferencia-se com @/%/$ antes da referência, ou com a seta -> para elementos: $arr->[0], $hash->{a}, $sub->(). A seta é a forma mais legível e recomendada.

Deep copy
use Storable qw(dclone);

my $original = { a => [1, 2, 3], b => { x => 1 } };

# Cópia superficial (partilha refs):
my $rasa = $original;

# Cópia profunda (independente):
my $copia = dclone($original);

$copia->{a}[0] = 99;
say $original->{a}[0];   # 1 (inalterado)

Atribuir uma referência copia o ponteiro, não os dados (alterações afetam ambos). Storable::dclone faz uma cópia profunda e independente — essencial quando precisa de modificar uma estrutura sem alterar a original.

Arrow em cadeia
my $dados = {
    pessoas => [
        { nome => "Ana", cidade => "Lisboa" },
        { nome => "Rui", cidade => "Porto" },
    ],
};

# Acesso encadeado:
my $cidade = $dados->{pessoas}[0]{cidade};
say $cidade;   # Lisboa

# A seta entre ] e { é opcional:
$dados->{pessoas}->[0]->{cidade};   # igual

Em acessos encadeados como $dados->{pessoas}[0]{cidade}, a seta entre ] e { é opcional — Perl infere o tipo automaticamente. Isto torna o acesso a estruturas complexas conciso e legível.

Weak references
use Scalar::Util qw(weaken);

my $a = { nome => "pai" };
my $b = { nome => "filho" };

$a->{filho} = $b;
$b->{pai} = $a;   # ciclo de referência!

weaken($b->{pai});   # não conta para o GC

# Agora $b->{pai} não impede $a de ser libertado

Referências fracas (weaken) não contam para o garbage collector — essenciais em estruturas circulares. Sem weaken, ciclos de referência nunca são libertados (memory leak), pois o reference counting do Perl não os deteta.

Expressões Regulares


12 cards
Match básico
my $texto = "Olá Mundo 123";

# Operador =~ (match):
if ($texto =~ /Mundo/) {
    say "Encontrou";
}

# Negação !~:
if ($texto !~ /xyz/) {
    say "Não achou";
}

# Em contexto booleano implícito ($_):
if (/padrão/) { say "em $_" }

O operador =~ aplica um padrão regex a uma string e devolve verdadeiro se encontrar. !~ nega. Sem =~, o match opera sobre $_ (a variável default). É a base de todo o processamento de texto em Perl.

Captura com parênteses
my $data = "2024-01-15";

if ($data =~ /(\d{4})-(\d{2})-(\d{2})/) {
    my ($ano, $mes, $dia) = ($1, $2, $3);
    say "Ano: $ano, Mês: $mes, Dia: $dia";
}

# Em contexto de lista:
my ($h, $m) = "14:30" =~ /(\d+):(\d+)/;

Parênteses capturam grupos para as variáveis $1, $2, $3... Em contexto de lista, o match devolve todas as capturas de uma vez. É a forma principal de extrair partes de uma string.

Transliteração tr///
my $texto = "Olá Mundo";

# Uppercase (range):
$texto =~ tr/a-z/A-Z/;   # "OLÁ MUNDO"

# Remover vogais (/d delete):
$texto =~ tr/aeiou//d;

# Contar ocorrências:
my $n = ($texto =~ tr/a//);   # nº de 'a'

# tr/// NÃO é regex:
# opera caractere a caractere

tr/// translitera caractere a caractere (não é regex). tr/a-z/A-Z/ converte para maiúsculas, /d remove os caracteres listados. Em contexto escalar devolve o número de caracteres substituídos — útil para contar.

Metacaracteres
/^inicio/      # início de linha
/fim$/         # fim de linha
/a.c/          # qualquer char entre a e c
/colou?r/      # u opcional (color/colour)
/ab+c/         # b uma ou mais vezes
/ab*c/         # b zero ou mais vezes
/a{2,4}/       # a entre 2 e 4 vezes
/\d+/          # um ou mais dígitos

Metacaracteres definem padrões: ^ início, $ fim, . qualquer carácter, ? opcional, + uma ou mais, * zero ou mais, {n,m} quantidade. \d é dígito. Cobrem 90% dos padrões comuns.

Capturas nomeadas
my $data = "2024-01-15";

if ($data =~ /(?<ano>\d{4})-(?<mes>\d{2})-(?<dia>\d{2})/) {
    say "$+{ano}/$+{mes}/$+{dia}";
}

# Mais legível que $1, $2, $3 em regex complexas
# %+ guarda as capturas nomeadas

Capturas nomeadas (?<nome>...) guardam o resultado em %+ (acedido por $+{nome}). São muito mais legíveis que $1, $2 em regex complexas, pois o nome documenta o significado de cada grupo.

Lookahead e lookbehind
# Lookahead positivo (?=):
/\d+(?=€)/        # número seguido de €

# Lookahead negativo (?!):
/\d+(?!€)/        # número NÃO seguido de €

# Lookbehind positivo (?<=):
/(?<=€)\d+/       # número precedido de €

# Lookbehind negativo (?<!):
/(?<!€)\d+/       # número NÃO precedido de €

# Não consomem caracteres

Lookahead (?=) e lookbehind (?<=) verificam o contexto sem consumir caracteres — validam formato sem capturar o delimitador. As versões negativas (?! e (?<! verificam ausência. Essenciais para validação precisa.

Classes de caracteres
/\d/       # dígito [0-9]
/\w/       # word char [a-zA-Z0-9_]
/\s/       # whitespace (espaço, tab, \n)
/\D/       # não-dígito
/\W/       # não-word char
/\S/       # não-whitespace

/[aeiou]/  # uma vogal
/[^0-9]/   # qualquer não-dígito
/[a-z]+/   # letras minúsculas

# Ponto não matcha \n por defeito:
/./s       # com /s, matcha \n também

Classes [] definem conjuntos de caracteres aceites; [^...] nega. Atalhos: \d dígito, \w word char, \s whitespace (e maiúsculas negam). O . matcha qualquer carácter exceto nova linha (a menos que use /s).

Substituição s///
my $texto = "Olá Mundo";

# Substituição simples:
$texto =~ s/Mundo/Perl/;   # "Olá Perl"

# Global (todas as ocorrências):
$texto =~ s/o/0/g;

# Com backreferences:
my $email = "ana@mail.com";
$email =~ s/(\w+)@(.+)/user: $1, dom: $2/;

# Modificadores: s///gi, s///e

s/// substitui texto: s/velho/novo/. O modificador /g substitui todas as ocorrências. Backreferences $1, $2 reutilizam grupos capturados no replacement. É a ferramenta central de transformação de texto.

qr// e padrões reutilizáveis
# qr// compila regex num objeto:
my $data_re = qr/\d{4}-\d{2}-\d{2}/;

if ($texto =~ $data_re) {
    say "Tem uma data";
}

# Composição de padrões:
my $num = qr/\d+/;
my $sep = qr/[\/-]/;
my $data2 = qr/$num$sep$num$sep$num/;

# Interpola em outras regex

qr// compila uma regex num objeto reutilizável e interpolável. Permite construir padrões complexos por composição (qr/$num$sep$num/) e partilhar regex entre várias operações. Aceita os mesmos modificadores.

Modificadores
/padrão/i    # case-insensitive
/padrão/g    # global (todas as ocorrências)
/padrão/m    # multiline (^ $ por linha)
/padrão/s    # . matcha \n também
/padrão/x    # permite espaços e comentários

# Exemplo /x (regex legível):
my $re = qr/
    ^ \d{4}   # ano
    - \d{2}   # mês
    - \d{2}   # dia
$/x;

Modificadores alteram o comportamento: /i ignora maiúsculas, /g encontra todas as ocorrências, /m faz ^/$ atuarem por linha, /x permite espaços e comentários para tornar regex legíveis. Combinam-se: /gix.

s///e (código no replacement)
# /e avalia o replacement como código Perl:
my $texto = "3 5 8";
$texto =~ s/(\d+)/$1 * 2/ge;
# "6 10 16"

# Uppercase cada palavra:
my $t = "olá mundo";
$t =~ s/(\w+)/ucfirst($1)/ge;
# "Olá Mundo"

# Sem /e, $1*2 seria literal

O modificador /e avalia o replacement como código Perl em vez de string — $1 * 2 calcula o dobro em vez de ficar literal. Combinado com /g, aplica a transformação a cada match. Ideal para transformações dinâmicas.

split com regex e \K
# split com padrão flexível:
my @campos = split(/\s*,\s*/, "a, b ,c");
# ("a", "b", "c")

# \K (reset do início do match):
my $s = "foobar";
$s =~ s/foo\Kbar/baz/;   # "foobaz"
# substitui "bar" mas mantém "foo"

# Equivalente a: s/(foo)bar/${1}baz/
# mas \K é mais simples

split aceita regex para separadores flexíveis (\s*,\s* ignora espaços). \K "esquece" o que matchou antes — em s/foo\Kbar/baz/ substitui só bar mantendo foo, simplificando o uso de backreferences.

OO e Módulos


12 cards
Package e módulo
# Ficheiro: Animal.pm
package Animal;
use strict;
use warnings;

sub new {
    my ($classe, %args) = @_;
    return bless {
        nome => $args{nome},
        som  => $args{som} // "...",
    }, $classe;
}

sub falar {
    my $self = shift;
    return "$self->{nome}: $self->{som}!";
}

1;   # módulos DEVEM terminar com true

Um módulo é um ficheiro .pm com um package correspondente ao caminho. bless associa uma referência hash a uma classe, criando um objeto. O construtor new é convenção. O 1; no fim é obrigatório — use verifica se o módulo retornou verdadeiro.

Moose (OO completo)
package Conta;
use Moose;

has titular => (
    is       => 'ro',
    isa      => 'Str',     # type constraint
    required => 1,
);

has saldo => (
    is      => 'rw',
    isa     => 'Num',
    default => 0,
);

sub levantar {
    my ($self, $valor) = @_;
    die "Saldo insuficiente" if $valor > $self->saldo;
    $self->saldo($self->saldo - $valor);
}

__PACKAGE__->meta->make_immutable;

Moose é o sistema OO mais completo: isa => 'Num' valida tipos automaticamente, make_immutable otimiza a classe. A penalidade é tempo de carregamento — por isso Moo existe como alternativa leve. Inspirou o OO moderno em Perl.

AUTOLOAD
package Config;
our $AUTOLOAD;

sub AUTOLOAD {
    my $self = shift;
    (my $metodo = $AUTOLOAD) =~ s/.*:://;
    if (@_) {
        $self->{$metodo} = shift;   # setter
    } else {
        return $self->{$metodo};    # getter
    }
}

my $cfg = bless {}, 'Config';
$cfg->nome("Ana");   # cria dinamicamente
say $cfg->nome;      # "Ana"

AUTOLOAD é chamado quando um método não existe — permite criar getters/setters dinâmicos. A variável $AUTOLOAD contém o nome do método chamado. É poderoso mas dificulta debugging; Moo/Moose geram accessors estaticamente.

Criar e usar objetos
use Animal;

# Criar objeto:
my $a = Animal->new(nome => "Rex", som => "Au");

# Chamar métodos (seta ->):
say $a->falar;   # "Rex: Au!"

# $self é o objeto (1º argumento):
sub falar {
    my $self = shift;   # retira o objeto
    return $self->{nome};
}

Objetos criam-se com Classe->new(...) e métodos chamam-se com a seta ->. O primeiro argumento de todo método é o próprio objeto ($self), retirado com shift. Os dados vivem no hash $self->{campo}.

Roles (composição)
# Role (comportamento reutilizável):
package Serializable;
use Moo::Role;

requires 'to_hash';   # método obrigatório

sub to_json {
    my $self = shift;
    return encode_json($self->to_hash);
}

# Consumo:
package User;
use Moo;
with 'Serializable';

sub to_hash { { nome => $_[0]->nome } }

Roles são composição de comportamento — a alternativa moderna à herança múltipla. requires declara métodos que o consumidor deve implementar; with aplica a role. Evitam o "diamond problem" da herança múltipla.

DESTROY
package Ligacao;

sub new { bless { dbh => conectar() }, shift }

# Destructor (chamado no garbage collection):
sub DESTROY {
    my $self = shift;
    $self->{dbh}->disconnect if $self->{dbh};
    say "Ligação fechada";
}

# Se usar AUTOLOAD, defina DESTROY vazio:
sub DESTROY { }   # impede AUTOLOAD

DESTROY é o destructor — chamado automaticamente quando o objeto é garbage collected. Essencial para fechar ligações e libertar recursos. Se usar AUTOLOAD, defina sempre um DESTROY (mesmo vazio) para o intercetar.

Herança
package Cao;
use parent 'Animal';   # herda de Animal

sub falar { "Au au" }   # override

package GuiaCao;
use parent 'Cao';

sub falar {
    my $self = shift;
    my $base = $self->SUPER::falar();   # chama o pai
    return "$base (cão guia)";
}

my $c = Cao->new(nome => "Rex");
say $c->isa('Animal');   # 1

use parent configura a herança (a lista @ISA). SUPER:: chama o método da classe pai. isa() verifica se um objeto é de uma classe (ou derivada). Perl suporta herança múltipla: use parent 'A', 'B'.

Encapsulamento
package Banco;
use Moo;

# setter privado (rwp):
has saldo => (
    is       => 'rwp',     # read-write privado
    init_arg => undef,     # não no construtor
    default  => 0,
);

sub depositar {
    my ($self, $valor) = @_;
    $self->_set_saldo($self->saldo + $valor);
}

# _set_saldo só é acessível dentro da classe

Perl não tem private nativo, mas Moo/Moose oferecem is => 'rwp' (setter privado _set_saldo) e init_arg => undef (exclui do construtor). A convenção _prefixo indica "não toque" nos dados internos.

Signatures (Perl 5.36+)
use v5.36;

# Signatures fazem destructuring automático:
sub soma ($a, $b) {
    return $a + $b;
}

# Com defaults e opcionais:
sub saudar ($nome, $prefixo = "Olá") {
    return "$prefixo, $nome!";
}

# Verifica aridade automaticamente:
soma(1, 2);      # OK
soma(1);         # ERRO (faltam args)

Signatures (Perl 5.36+) são a forma moderna de declarar parâmetros: sub soma ($a, $b) faz destructuring de @_ automaticamente e verifica a aridade. Substituem os protótipos antigos e o my ($a, $b) = @_ manual.

Moo (OO moderno leve)
package Pessoa;
use Moo;

has nome => (
    is       => 'ro',     # read-only
    required => 1,
);

has idade => (
    is      => 'rw',      # read-write
    default => sub { 0 },
);

# Uso:
my $p = Pessoa->new(nome => "Ana", idade => 30);
say $p->nome;        # Ana (getter)
$p->idade(31);       # setter (rw)

Moo é OO moderno minimalista. has declara atributos com accessors automáticos: is => 'ro' cria só getter, 'rw' cria getter e setter. required => 1 torna obrigatório; default define valor por defeito. É compatível com Moose.

Sobrecarga de operadores
package Vector;
use overload
    '+'  => sub {
        my ($self, $outro) = @_;
        Vector->new($self->{x} + $outro->{x},
                    $self->{y} + $outro->{y});
    },
    '""' => sub { "($_[0]{x}, $_[0]{y})" },
    fallback => 1;

my $a = Vector->new(1, 2);
my $b = Vector->new(3, 4);
my $c = $a + $b;   # Vector(4, 6)
say $c;            # "(4, 6)"

use overload faz objetos comportarem-se como valores nativos: + soma, "" converte para string (usado em say). fallback => 1 permite derivar operadores não definidos. Essencial para classes matemáticas.

Métodos de classe vs objeto
package Contador;
my $total = 0;   # variável de classe

sub new { bless {}, shift }

# Método de classe (chamado na classe):
sub total_geral {
    return $total;
}

# Método de objeto (chamado na instância):
sub incrementar {
    my $self = shift;
    $total++;
}

Contador->incrementar();      # $self = "Contador"
my $c = Contador->new();
$c->incrementar();            # $self = objeto
say Contador->total_geral();  # 2

Em Perl, métodos de classe e de objeto usam a mesma sintaxe ->. A diferença está no primeiro argumento: na classe chega o nome da classe, na instância chega o objeto. shift retira-o em ambos os casos.

Ficheiros e I/O


10 cards
open para leitura
# Three-argument form (seguro):
open(my $fh, "<", "ficheiro.txt")
    or die "Não abri: $!";

# Ler tudo:
my @linhas = <$fh>;

# Ou linha a linha:
while (my $linha = <$fh>) {
    chomp $linha;
    say $linha;
}

close $fh;

open com três argumentos (open $fh, MODO, FICHEIRO) é o padrão seguro — evita injeção de nomes de ficheiro. < é leitura. or die $! termina com a mensagem de erro do sistema se falhar. Sempre feche com close.

Operador diamante
# Lê dos ficheiros em @ARGV ou STDIN:
while (my $linha = <>) {
    chomp $linha;
    print "$.: $linha\n";   # $. = nº da linha
}

# Uso: perl script.pl f1.txt f2.txt
# ou:  cat f.txt | perl script.pl

# In-place editing:
# perl -pi -e 's/foo/bar/g' *.txt

O operador diamante <> lê linhas dos ficheiros passados em @ARGV ou do STDIN — a base dos filtros de texto Unix. $. é o número da linha atual. Com -pi -e faz edição in-place de ficheiros.

Diretórios
use File::Path qw(make_path remove_tree);
use File::Copy qw(copy move);

# Criar pasta (recursivo):
make_path("dados/logs/2024");

# Listar conteúdo:
opendir(my $dh, "dados") or die $!;
my @ficheiros = grep { !/^\./ } readdir($dh);
closedir $dh;

# Copiar / mover:
copy("a.txt", "b.txt") or die $!;
move("b.txt", "arquivo/b.txt");

File::Path cria e remove pastas recursivamente (make_path). opendir/readdir listam conteúdo (filtre . e .. com grep). File::Copy copia e move ficheiros. São módulos core do Perl.

open para escrita e append
# Escrita (trunca o ficheiro):
open(my $out, ">", "saida.txt")
    or die "Não criei: $!";
print $out "Linha $_\n" for 1..10;
close $out;

# Append (acrescenta ao fim):
open(my $log, ">>", "app.log")
    or die "Erro: $!";
say $log "[" . localtime . "] evento";
close $log;

> abre para escrita (apaga o conteúdo existente); >> abre para append (acrescenta ao fim). print $out e say $log escrevem no filehandle. Sempre use or die para tratar falhas de abertura.

Variáveis especiais de I/O
$!    # erro do sistema (errno)
$.    # número da linha do último handle
$_    # variável default
$/    # separador de entrada (default \n)
$\    # separador de saída
$|    # autoflush (1 = sem buffer)

# Ler parágrafos:
local $/ = "";        # modo parágrafo
while (my $par = <$fh>) { ... }

# Slurp completo:
local $/ = undef;
my $tudo = <$fh>;

Perl tem variáveis especiais de I/O: $! é o erro do sistema, $. o número da linha, $/ o separador de entrada. Mudar $/ para "" lê por parágrafos; para undef lê o ficheiro inteiro (slurp).

File::Find (recursivo)
use File::Find;

# Percorrer árvore de pastas:
find(\&processar, "dados");

sub processar {
    return unless -f $_;        # só ficheiros
    return unless /\.pm$/;      # só .pm
    say "$File::Find::name";    # caminho completo
}

# Encontrar ficheiros grandes:
find(sub {
    say "$File::Find::name" if -f && -s > 1_000_000;
}, ".");

File::Find percorre recursivamente uma árvore de diretórios, chamando uma sub para cada entrada. $_ é o nome e $File::Find::name o caminho completo. Os operadores -f (ficheiro) e -s (tamanho) filtram os resultados.

Encoding UTF-8
# Leitura com encoding:
open(my $fh, "<:encoding(UTF-8)", "f.txt")
    or die "Erro: $!";

# Escrita com encoding:
open(my $out, ">:encoding(UTF-8)", "o.txt")
    or die "Erro: $!";

# Pragmas para todo o script:
use open ':std', ':encoding(UTF-8)';
use utf8;   # código-fonte em UTF-8

A camada :encoding(UTF-8) garante leitura/escrita correta de Unicode. Sem ela, caracteres acentuados podem corromper-se. use utf8 declara que o código-fonte está em UTF-8; use open aplica encoding a todos os handles.

STDIN, STDOUT, STDERR
# Ler do teclado:
print "Nome: ";
my $nome = <STDIN>;
chomp $nome;

# Escrever no erro:
say STDERR "Atenção!";

# Redirecionar STDOUT:
open(my $salvo, ">&", \*STDOUT);
open(STDOUT, ">", "log.txt");
say "vai para o ficheiro";
open(STDOUT, ">&", $salvo);   # restaurar

STDIN, STDOUT e STDERR são os filehandles padrão (entrada, saída, erro). say STDERR escreve no erro sem afetar o output normal. Podem ser redirecionados para ficheiros com open.

Path::Tiny
use Path::Tiny;

# Ler ficheiro completo:
my $conteudo = path("f.txt")->slurp_utf8;

# Escrever:
path("out.txt")->spew_utf8($conteudo);

# Linhas:
my @linhas = path("f.txt")->lines_utf8;

# Outras operações:
path("dados")->mkpath;          # criar pasta
path("f.txt")->exists;          # existe?
path("f.txt")->basename;        # "f.txt"

Path::Tiny (CPAN) moderniza o I/O de ficheiros: slurp_utf8 lê tudo, spew_utf8 escreve, com encoding automático. É muito mais conciso que open/close manual e trata erros de forma previsível.

die e errno
# Padrão idiomático de erro:
open(my $fh, "<", $file)
    or die "Não abri '$file': $!";

# $! contém a mensagem do sistema:
# "No such file or directory"

# die com código de saída:
die "Erro fatal\n";   # exit code != 0

# warn (não termina):
warn "Aviso: $!" if $problema;

or die é o padrão de tratamento de erro em Perl: tenta a operação ou termina com mensagem. $! contém a descrição do erro do sistema. die termina o programa; warn só imprime um aviso e continua.

Dicas e Boas Práticas


8 cards
strict e warnings sempre
# Sempre no topo de cada script/módulo:
use strict;
use warnings;

# Ou moderno (ativa ambos):
use v5.36;

# strict: typos viram erros de compilação
# warnings: operações suspeitas geram avisos

# Sem eles, bugs silenciosos:
$nom = "Ana";   # typo! variável global

use strict e use warnings são obrigatórios em Perl moderno. strict transforma typos em variáveis em erros de compilação; warnings alerta sobre operações suspeitas. use v5.36 ativa ambos automaticamente.

Convenções de nomenclatura
$escalar    # variável escalar
@array      # array
%hash       # hash
&sub        # subrotina (raro)

# Nomes:
my $contador_total;     # snake_case
sub calcular_desconto;  # snake_case
package Minha::Classe;  # PascalCase

# Constantes:
use constant MAX => 100;
# ou
Readonly my $MAX => 100;

Convenções Perl: variáveis e subs em snake_case, packages em PascalCase (com :: como separador). O sigilo ($, @, %) indica o tipo. use constant cria constantes. Prefira nomes descritivos.

// vs ||
my $porta = 0;

# || trata 0 e "" como falsos (PERIGO):
my $a = $porta || 3306;   # 3306 (errado!)

# // só usa default se undef (SEGURO):
my $b = $porta // 3306;   # 0 (correto!)

# Regra: use // para valores que podem
# legitimamente ser 0 ou string vazia

A distinção || vs // é crítica: || trata 0, "" e "0" como falsos (devolve o default erradamente), enquanto // só ativa para undef. Use // para valores que podem legitimamente ser zero ou string vazia.

Módulos essenciais
# Sempre úteis (core):
use List::Util qw(sum max first);
use Scalar::Util qw(blessed reftype);
use File::Path qw(make_path);
use File::Basename qw(basename);
use Getopt::Long;
use POSIX qw(strftime);

# CPAN recomendados:
use Path::Tiny;      # I/O moderno
use Try::Tiny;       # exceções
use JSON;            # JSON
use DateTime;        # datas

Estes módulos resolvem 90% das tarefas comuns: List::Util e Scalar::Util (core) para coleções e tipos; Path::Tiny, Try::Tiny, JSON e DateTime (CPAN) modernizam I/O, exceções, serialização e datas.

$_ a variável default
# $_ é usada quando nada é especificado:
for (@lista) { say }        # say $_
print if /padrão/;          # $_ =~ /padrão/
my $x = length;             # length($_)

# map/grep usam $_:
my @d = map { $_ * 2 } @n;

# Cuidado: $_ é global e partilhada
# Em código complexo, use variáveis nomeadas

$_ é a variável default de Perl — usada por print, say, map, grep, regex e muitas funções quando nenhum argumento é dado. Torna o código conciso, mas em excesso reduz a clareza. Use com moderação.

Debugging
# Inspecionar variáveis:
use Data::Dumper;
warn Dumper($estrutura);

# warn não termina (bom para debug):
warn "Aqui: $valor\n";

# Depurador interativo:
# perl -d script.pl

# Comandos do depurador:
# s (step), n (next), p $var (print)
# b 10 (breakpoint linha 10), c (continue)

Data::Dumper + warn é a forma rápida de inspecionar variáveis sem parar o programa. Para debugging sério, perl -d abre um depurador interativo com breakpoints (b), step (s) e inspeção (p).

One-liners
# Substituir em ficheiros (-pi -e):
perl -pi -e 's/foo/bar/g' *.txt

# Imprimir linhas que matcham:
perl -ne 'print if /erro/' log.txt

# Somar a 2ª coluna:
perl -lane '$s += $F[1]; END { say $s }' dados

# -n: while(<>){...}  -l: chomp + \n
# -a: split em @F     -e: código

One-liners Perl são poderosos para processamento de texto: -n envolve em while(<>), -p adiciona print, -l faz chomp e nova linha, -a divide em @F, -i edita in-place. Substituem awk/sed em muitas tarefas.

Boas práticas gerais
# 1. strict + warnings sempre
use v5.36;

# 2. // em vez de || para defaults
my $x = $input // "default";

# 3. Three-arg open + or die
open(my $fh, "<", $f) or die $!;

# 4. Nomes descritivos, não $_ em excesso
for my $cliente (@clientes) { ... }

# 5. Módulos em vez de reinventar
use List::Util qw(sum);

Boas práticas Perl: ative strict/warnings, use // para defaults, open com três argumentos e or die, nomes descritivos em vez de $_ em excesso, e módulos do CPAN em vez de reinventar a roda. Código Perl deve ser legível, não "mágico".