DevTools

Cheatsheet Perl

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

Volver a los lenguajes
Perl
112 tarjetas encontradas
Categorías:
Versiones:

Básico e Variáveis


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

# Mi primer script
say "¡Hola Mundo!";

my $nombre = "Ana";
say "¡Hola, $nombre!";

Un script Perl comienza con el shebang #!/usr/bin/env perl. Los pragmas use strict y use warnings son obligatorios en código moderno. say imprime con nueva línea automática.

Strings y operadores
my $s = "Hola" . " Mundo";   # concatenación
my $rep = "ab" x 3;          # "ababab"
my $len = length($s);        # 10

uc("texto");      # "TEXTO"
lc("TEXTO");      # "texto"
ucfirst("nombre"); # "Nombre"
substr($s, 0, 4); # "Hola"
index($s, "Mundo"); # posición

El operador . concatena strings y x repite. Funciones como uc, lc, substr e index manipulan texto. length devuelve el tamaño. Las comillas dobles interpolan variables; las comillas simples son literales.

Operadores especiales (// y or)
# Defined-or (//):
my $val = $input // "default";
# usa $input si está definido (incluso 0 o "")

# Or de baja precedencia:
open(my $fh, "<", $file) or die "Error: $!";

# Ternario:
my $status = $edad >= 18 ? "adulto" : "menor";

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

El operador // (defined-or) devuelve el operando derecho solo si el izquierdo es undef — al contrario que || que también trata 0 y "" como falsos. or die es el patrón idiomático de tratamiento de errores.

Variables escalares ($)
my $nombre = "Ana";
my $edad = 30;
my $pi = 3.14159;
my $activo = 1;      # verdadero
my $vacio = undef;   # sin valor

# Interpolación en comillas dobles:
print "¡Hola, $nombre! Tienes $edad años.\n";

# Concatenación con punto:
my $msg = $nombre . " tiene " . $edad;

Los escalares ($) guardan un único valor: string, número o undef. Perl convierte automáticamente según el operador — . concatena, + suma. my declara variables léxicas con ámbito limitado al bloque.

qw y here-doc
# qw (quote words) - lista de palabras:
my @colores = qw(rojo verde azul);
# igual a ("rojo", "verde", "azul")

# Here-doc (string multi-línea):
my $texto = <<FIN;
Linea 1
Linea 2 con $nombre
FIN

# sprintf (formateo):
my $fmt = sprintf("%.2f", 3.14159);  # "3.14"

qw() crea listas de palabras sin comillas ni comas. El here-doc <<FIN permite strings multi-línea con interpolación. sprintf formatea números y strings con control total de precisión.

Pragmas
use strict;     # exige declaración de variables
use warnings;   # avisa sobre problemas
use v5.36;      # versión moderna (incluye ambos)
use utf8;       # código fuente en UTF-8
use feature 'say';  # say = print + \n

# Con use v5.36+:
use v5.36;
say "Hola sin \n manual";

# Desactivar temporalmente:
no warnings 'numeric';
my $n = "12abc" + 0;   # sin warning

use strict impide variables no declaradas (los typos se vuelven errores). use warnings alerta sobre operaciones sospechosas. use v5.36 activa ambos más funcionalidades modernas como say. Estos pragmas transforman bugs silenciosos en errores detectables.

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

# Acceso (índice empieza en 0):
my $primero = $frutas[0];   # "manzana"
my $último = $frutas[-1];   # "uva"

# Tamaño:
my $tamano = scalar @frutas;   # 3

Los arrays (@) son listas ordenadas con índice que empieza en 0. El sigilo cambia: @frutas para la lista, $frutas[0] para un elemento. Los índices negativos cuentan desde el final (-1 es el último).

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

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

# Asignación en lista:
my ($a, $b, $c) = (10, 20, 30);
my ($nombre, $edad) = split(/,/, "Ana,30");

El concepto de contexto es central en Perl: la misma expresión se comporta de forma diferente en contexto escalar vs lista. Un array en contexto escalar devuelve el tamaño; en contexto de lista devuelve los elementos.

undef y defined
my $x = undef;   # sin valor

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

# Definir con defined-or:
$x //= 42;   # solo si $x es undef

# undef como función:
undef $x;    # limpia la variable
my @arr = ();
undef @arr;  # vacía el array

undef representa la ausencia de valor (como null). defined verifica si una variable tiene valor. El operador //= asigna solo si es undef. No confundas undef con 0 o string vacío — son valores definidos.

Hashes (%)
my %persona = (
    nombre => "Ana",
    edad   => 30,
    ciudad => "Lisbon",
);

# Acceso:
my $nombre = $persona{nombre};
$persona{email} = "ana@mail.com";

# Claves y valores:
my @claves = keys %persona;
my @valores = values %persona;

Los hashes (%) son diccionarios clave/valor. La sintaxis clave => valor es azúcar para ("clave", "valor"). keys devuelve las claves y values los valores. El acceso se hace con llaves: $persona{nombre}.

Operador diamante y chomp
# Leer líneas de ficheros/STDIN:
while (my $linea = <>) {
    chomp $linea;   # elimina el \n final
    print $linea;
}

# chomp vs chop:
my $s = "Hola\n";
chomp $s;   # elimina solo el \n final
chop $s;    # elimina el último carácter

El operador diamante <> lee líneas de los ficheros pasados como argumentos o del STDIN. chomp elimina la nueva línea final (esencial al leer input). chop elimina el último carácter, sea cual sea.

say vs print
# print (sin nueva línea automática):
print "Hola";
print "Mundo\n";   # \n manual

# say (con nueva línea, Perl 5.10+):
say "Hola";    # imprime "Hola\n"
say "Mundo";

# print con filehandle:
print $fh "linea\n";

# say a stderr:
say STDERR "¡Error!";

say es como print pero añade nueva línea automáticamente (requiere use feature 'say' o use v5.36). print es más flexible para output sin salto de línea. Ambos aceptan un filehandle como primer argumento.

Subrotinas e Funções


12 cards
Subrutina básica
# Definición:
sub saludo {
    my ($nombre, $hora) = @_;
    return "¡Hola, $nombre! ($hora)";
}

# Llamada:
my $msg = saludo("Ana", "mañana");
say $msg;

# Sin paréntesis (idiomático):
saludo "Ana", "tarde";

Las subrutinas se definen con sub. Los argumentos llegan vía @_ y el patrón my ($a, $b) = @_ hace destructuring. return devuelve el valor (la última expresión es el retorno implícito).

Closures
sub crear_contador {
    my $count = 0;   # variable léxica privada
    return sub {
        $count++;
        return $count;
    };
}

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

# Cada contador es independiente:
my $d = crear_contador();
say $d->();   # 1

Las closures capturan variables léxicas del ámbito donde fueron creadas — $count "vive" dentro de la sub anónima incluso después de que crear_contador() retorne. Cada llamada produce un contador independiente.

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

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

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

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

# Por campo de hash:
my @por_edad = sort {
    $a->{edad} <=> $b->{edad}
} @personas;

sort ordena con un comparador custom. $a y $b son los dos elementos comparados; el bloque devuelve negativo, cero o positivo. Usa <=> para números y cmp para strings.

@_ y parámetros
# @_ contiene todos los argumentos:
sub demo {
    say "Total: " . scalar @_;
    say "Primero: $_[0]";
    say "Último: $_[-1]";
}

# Destructuring con defaults:
sub conectar {
    my ($host, $puerto) = @_;
    $puerto //= 3306;   # valor por defecto
}

@_ es el array especial con los argumentos pasados. $_[0] es el primero, $_[-1] el último. El operador //= define valores por defecto para parámetros opcionales.

state
use feature 'state';

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

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

state (Perl 5.10+) crea variables que persisten entre llamadas sin ser globales. La inicialización ocurre solo en la primera llamada. Es ideal para contadores y caches privados.

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

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

my $suma = sum @nums;       # 23
my $mayor = max @nums;      # 9
my $menor = min @nums;      # 1

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

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

List::Util (core) elimina loops manuales: sum, max, min hacen agregaciones; first devuelve el primer elemento que satisface el test; reduce es el acumulador genérico para cualquier operación binaria.

Hash de opciones (named params)
sub crear_usuario {
    my %opts = @_;
    my $nombre = $opts{nombre} // die "nombre obligatorio";
    my $email  = $opts{email};
    my $admin  = $opts{admin} // 0;
    say "$nombre ($email)";
}

# Llamada auto-documentada:
crear_usuario(nombre => "Ana", admin => 1);

El patrón de hash de opciones (%opts = @_) simula parámetros nombrados: la llamada queda auto-documentada (nombre => "Ana") y el orden no importa. Es el patrón preferido para funciones con muchos parámetros.

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

# Extraer campo de hashes:
my @nombres = map { $_->{nombre} } @personas;

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

# $_ es el elemento actual

map transforma cada elemento de una lista, pudiendo producir 0, 1 o N resultados por input. $_ es el elemento actual. Es ideal para extraer campos de hashes o construir nuevas estructuras.

Recursión
sub factorial {
    my ($n) = @_;
    return 1 if $n <= 1;
    return $n * factorial($n - 1);
}

say factorial(5);   # 120

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

Perl soporta recursión de forma natural. Toda función recursiva necesita un caso base (return 1 if $n <= 1) para parar. Para recursión profunda, prefiere iteración — Perl no optimiza tail-calls.

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

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

# Referencia a sub nombrada:
my $ref = \&saludo;
$ref->("Ana", "noche");

Las subs anónimas (sub { }) son valores de primera clase, guardadas en escalares y pasadas como argumentos. Se llaman con la flecha ->(). \&nombre crea una referencia a una sub nombrada.

grep
# Filtrar elementos:
my @mayores = grep { $_ > 18 } @edades;

my @activos = grep { $_->{activo} } @users;

# En contexto booleano (verificar existencia):
if (grep { $_ > 100 } @valores) {
    say "Hay valores grandes";
}

# $_ es el elemento probado

grep filtra una lista, devolviendo solo los elementos que pasan el test (el bloque devuelve verdadero). $_ es el elemento probado. En contexto booleano verifica si algún elemento satisface la condición.

Memoización
use Memoize;
memoize('fibonacci');

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

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

La memoización guarda en caché los resultados de funciones puras — fibonacci(40) pasa de minutos a instantáneo. El módulo Memoize hace esto automáticamente; la versión manual usa un hash con //=.

Avançado


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

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

# $@ contiene el mensaje del die
# eval devuelve undef si hay error

Perl usa eval { } + die como mecanismo de excepciones nativo. eval captura el die más cercano y guarda el mensaje en $@. Si no hay error, $@ queda vacío. Es la base del manejo de excepciones en Perl.

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

# Módulos esenciales (core):
use Getopt::Long;    # args de línea de comandos
use File::Basename;  # basename/dirname
use POSIX qw(strftime ceil);
use Time::Piece;     # fechas

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

CPAN tiene 200.000+ módulos; cpanm los instala fácilmente. Los módulos core vienen con Perl (Getopt::Long, POSIX, Time::Piece). DBI es la interfaz universal de bases de datos; LWP hace peticiones HTTP; Mojolicious es un framework web.

Tied variables
# Tie de un hash a fichero (persistencia):
use DB_File;
tie my %db, 'DB_File', "datos.db"
    or die "No pude abrir: $!";
$db{clave} = "valor";   # ¡persiste!
untie %db;

# Tie::File (fichero como array):
use Tie::File;
tie my @lineas, 'Tie::File', "f.txt";
$lineas[0] = "Nueva primera línea";
untie @lineas;

tie asocia una variable a una clase que intercepta todas las operaciones — la variable parece normal pero el comportamiento es custom. DB_File persiste hashes en disco; Tie::File trata un fichero como array (cada línea es un elemento).

Try::Tiny
use Try::Tiny;

try {
    operacion_peligrosa();
} catch {
    warn "Error: $_";   # $_ tiene el mensaje
} finally {
    cleanup();
};

# Más seguro que eval/$@:
# resuelve problemas sutiles de localización de $@

Try::Tiny ofrece sintaxis try/catch/finally limpia y resuelve problemas sutiles del eval/$@ (localización, objetos). El mensaje del error queda en $_ dentro del catch. Es la forma recomendada de tratar excepciones en 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 fichero]\n";

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

Getopt::Long parsea argumentos de línea de comandos: =s espera string, |v crea alias corto. Rellena variables por referencia. Es el estándar para scripts con opciones (--verbose, --output=fichero).

Data::Dumper
use Data::Dumper;

my $datos = {
    users => [ { nombre => "Ana" }, { nombre => "Ray" } ],
    total => 2,
};

# Inspección para debugging:
print Dumper($datos);

# Configuración útil:
$Data::Dumper::Sortkeys = 1;   # ordenar claves
$Data::Dumper::Indent = 1;     # indentación simple

Data::Dumper imprime la estructura completa de cualquier referencia en formato Perl legible — indispensable para el debugging de estructuras complejas. Sortkeys ordena las claves para un output consistente.

Carp
use Carp qw(croak carp confess cluck);

# croak = die en la línea del LLAMADOR:
sub dividir {
    my ($a, $b) = @_;
    croak "División por cero" if $b == 0;
}

# carp = warn en la línea del llamador:
carp "Valor sospechoso";

# confess = die con stack trace completo:
confess "Error grave";

Carp mejora los mensajes de error en módulos: croak reporta el error en la línea del llamador (no del módulo), carp hace lo mismo para avisos, confess incluye el stack trace completo. Esencial para código de producción.

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

if ($pid == 0) {
    # proceso hijo
    trabajo_pesado();
    exit 0;
} elsif ($pid > 0) {
    # proceso padre
    waitpid($pid, 0);   # espera al hijo
    say "Hijo terminó";
} else {
    die "fork falló: $!";
}

fork() crea un proceso hijo con memoria independiente (sin race conditions). Devuelve 0 en el hijo y el PID en el padre. waitpid espera a que el hijo termine. Es el modelo de concurrencia más natural y seguro en Perl.

JSON
use JSON;

my $datos = { nombre => "Ana", edad => 30 };

# Serializar:
my $json = encode_json($datos);
# {"nombre":"Ana","edad":30}

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

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

El módulo JSON convierte entre estructuras Perl y JSON: encode_json serializa, decode_json deserializa. Los hashes se vuelven objetos JSON y los arrays listas. pretty formatea con indentación para lectura.

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

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

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

# use se evalúa antes de ejecutar

use carga e importa en tiempo de compilación (los errores paran la compilación); require carga en tiempo de ejecución (para módulos opcionales). use Módulo qw(...) es equivalente a require + import. Usa require para dependencias condicionales.

Parallel::ForkManager
use Parallel::ForkManager;

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

for my $url (@urls) {
    $pm->start and next;   # fork (el padre salta)
    download($url);        # solo en el hijo
    $pm->finish;           # exit del hijo
}

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

Parallel::ForkManager simplifica pools de workers con límite de procesos. $pm->start hace fork (el padre salta con and next); el hijo ejecuta y llama a $pm->finish. Ideal para procesar muchas tareas en 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);

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

DateTime (CPAN) es la forma robusta de trabajar con fechas: ymd/hms formatean, add suma tiempo, clone evita mutación. delta_days calcula diferencias. Muy superior a localtime para cálculos de fechas.

Arrays e Hashes


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

push @cola, 4;       # añade al final → (1,2,3,4)
pop @cola;           # elimina del final → (1,2,3)
unshift @cola, 0;    # añade al inicio → (0,1,2,3)
shift @cola;         # elimina del inicio → (1,2,3)

# push múltiples:
push @cola, (5, 6, 7);

push/pop manipulan el final del array; unshift/shift manipulan el inicio. push y unshift aceptan varios elementos. shift se usa mucho para sacar el primer argumento en subrutinas.

each / keys / values
my %persona = (nombre => "Ana", edad => 30);

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

# Solo claves / solo valores:
for my $k (keys %persona) {
    say "$k => $persona{$k}";
}
my @vals = values %persona;

# exists / delete:
exists $persona{nombre};   # True
delete $persona{edad};     # elimina

each itera par a par (más eficiente en hashes grandes). keys y values devuelven listas. exists verifica si una clave existe (incluso con valor undef); delete elimina la entrada.

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

# Merge (b tiene precedencia):
my %merged = (%a, %b);   # x=1, y=3, z=4

# Merge sin sobrescribir:
my %safe = (%b, %a);     # y queda en 2

# Añadir solo claves nuevas:
for my $k (keys %b) {
    $a{$k} //= $b{$k};
}

El merge de hashes se hace con (%a, %b) — en claves duplicadas, gana el segundo hash. Para no sobrescribir, invierte el orden o usa //= para rellenar solo las claves que faltan.

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

# Insertar en medio:
splice @arr, 2, 0, (3, 4);   # (1,2,3,4,5,6)

# Eliminar elementos:
splice @arr, 1, 2;           # elimina 2 elems

# Sustituir:
splice @arr, 0, 1, (9, 9);   # cambia el primero

# splice @arr, offset, length, @reemplazo

splice es la operación más versátil de arrays: inserta, elimina y sustituye elementos en cualquier posición. La sintaxis es splice @arr, offset, length, @reemplazo. Con length 0 solo inserta; sin reemplazo solo elimina.

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

exists $h{a};     # True (la clave existe)
exists $h{b};     # True (la clave existe, valor undef)
exists $h{c};     # False (la clave no existe)

defined $h{a};    # True (valor no-undef)
defined $h{b};    # False (el valor es undef)
defined $h{c};    # False

# delete vs undef:
delete $h{b};     # elimina la clave
$h{b} = undef;    # la clave queda, valor undef

exists verifica si la CLAVE existe; defined verifica si el VALOR no es undef. Una clave puede existir con valor undef (exists true, defined false). delete elimina la clave; asignar undef la mantiene.

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

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

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

Para ordenar un hash por el valor, se ordenan las keys comparando $hash{$b} <=> $hash{$a} (descendente) o $hash{$a} <=> $hash{$b} (ascendente). El resultado es una lista de claves ordenadas.

slice
my @frutas = qw(manzana banana uva pera kiwi);

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

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

# Asignación vía slice:
@h{qw(x y)} = (10, 20);

Los slices acceden a varios elementos a la vez. Nota el sigilo: @frutas[0,2] (array slice usa @) vs $frutas[0] (un elemento usa $). Los hash slices usan @h{qw(...)}.

Hash de hashes
my %datos;
$datos{Ana}{edad} = 30;
$datos{Ana}{ciudad} = "Lisbon";
$datos{Ray}{edad} = 25;

# Acceso:
say $datos{Ana}{ciudad};   # Lisbon

# Iterar:
for my $nombre (keys %datos) {
    my $info = $datos{$nombre};
    say "$nombre: $info->{edad} años";
}

Los hashes de hashes son la estructura más común para datos estructurados en Perl. La sintaxis $datos{Ana}{edad} es auto-vivificante: crea los hashes intermedios automáticamente al asignar.

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;   # elimina duplicados
any { $_ > 8 } @nums;      # True (alguno > 8)
all { $_ > 0 } @nums;      # True (todos > 0)
none { $_ < 0 } @nums;     # True (ninguno < 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) extiende List::Util: uniq elimina duplicados, any/all/none prueban condiciones, mesh intercala listas. Complementan las funciones de orden superior nativas.

join y 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+/, $linea);   # por whitespace

# split con límite:
my @top3 = split(/,/, $csv, 3);

join construye una string a partir de una lista con un separador. split hace lo inverso: divide una string en lista por un patrón (regex). Son operaciones inversas y muy usadas para CSV y parsing.

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

# Acceso:
say $clases{A}[0];   # Ana

# Iterar:
for my $clase (keys %clases) {
    say "Clase $clase:";
    say "  $_" for @{$clases{$clase}};
}

Los hashes de arrays agrupan listas por clave. push @{$clases{A}} añade al array de la clave A (la referencia se desreferencia con @{ }). El acceso combina hash y array: $clases{A}[0].

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

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

# Recorrer:
for my $fila (@matriz) {
    for my $celda (@$fila) {
        print "$celda ";
    }
    say "";
}

Perl no tiene arrays multidimensionales nativos — se usan arrays de referencias a arrays. Cada fila es una referencia [...]. El acceso encadena índices: $matriz[1][2]. @$fila desreferencia la fila.

Controlo de Fluxo


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

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

La estructura if/elsif/else testea condiciones en orden. elsif (no elseif) encadena múltiples condiciones. Los paréntesis en la condición son opcionales pero recomendados.

while / until
# while (mientras verdadero):
while (my $linea = <$fh>) {
    chomp $linea;
    procesar($linea);
}

# until (mientras falso):
until ($done) {
    $done = intentar();
}

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

while repite mientras la condición sea verdadera; until repite mientras sea falsa (el complemento). while (<$fh>) es el patrón clásico de lectura línea a línea de un fichero.

Comparación numérica vs string
# Numéricos:
==  !=  <  >  <=  >=

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

# El error clásico:
"10" == "9"   # FALSE (numérico: 10 != 9)
"10" lt "9"   # TRUE  (string: "1" < "9")

# Usa siempre el operador correcto:
if ($edad >= 18) { }    # numérico
if ($nombre eq "Ana") { } # string

Perl separa operadores numéricos (==, <) de operadores de string (eq, lt). Usar el equivocado es un bug clásico: "10" lt "9" es verdadero porque compara lexicográficamente. Usa numéricos para números y eq/lt para strings.

unless
my $error = 0;

# unless = if negado:
unless ($error) {
    say "Todo OK";
}

# Equivalente a:
if (!$error) {
    say "Todo OK";
}

# Statement modifier:
say "Sin error" unless $error;

unless ejecuta el bloque cuando la condición es FALSA — más legible que if (!$cond) para verificaciones de ausencia. Como statement modifier al final de la línea, se lee naturalmente: "haz esto a menos que...".

do-while
my $input;

# Se ejecuta al menos una vez:
do {
    print "Comando: ";
    $input = <STDIN>;
    chomp $input;
} while ($input ne "salir");

say "¡Adiós!";

El bloque do { } while ejecuta el cuerpo antes de probar la condición, garantizando al menos una ejecución. Es útil para menús y validación de input del usuario.

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

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

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

El operador spaceship <=> compara números y cmp compara strings, devolviendo -1, 0 o 1. Son la base de los bloques de ordenación en sort.

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

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

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

Los statement modifiers (if, unless, for, while al final de la línea) son idiomáticos para acciones de una línea. La lectura queda "acción si condición", natural como el inglés. Ideales para código conciso.

next / last / redo
foreach my $n (1..20) {
    next if $n % 2 == 0;   # salta pares
    last if $n > 15;       # sale del loop
    say $n;                # solo impares hasta 15
}

# redo (reinicia la iteración):
foreach my $item (@datos) {
    if (!válido($item)) {
        $item = corregir($item);
        redo;   # repite con $item corregido
    }
}

next salta a la siguiente iteración, last sale del loop completamente, y redo reinicia la iteración actual sin avanzar. Son el equivalente a continue, break y un "retry" de otros lenguajes.

Operador ternario
my $edad = 20;

# condición ? valor_si_true : valor_si_false
my $status = $edad >= 18 ? "adulto" : "menor";
say $status;   # adulto

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

# Mejor con if/elsif para claridad

El operador ternario ?: elige entre dos valores en una expresión. Es útil para asignaciones simples, pero encadenar varios ternarios reduce la legibilidad — prefiere if/elsif en esos casos.

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

# for corto con $_ (alias):
say $_ * 2 for 1..10;

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

# for y foreach son sinónimos:
for my $x (@nums) { say $x }

foreach itera sobre listas sin índices; la variable de iteración es un alias del elemento original. for y foreach son sinónimos en Perl. $_ es la variable por defecto cuando no se declara ninguna.

continue
while (<>) {
    next if /^#/;      # salta comentarios
    procesar($_);
} continue {
    $. = 0 if eof;     # reset del contador de líneas
}

El bloque continue se ejecuta después de cada iteración del loop, incluso cuando se usa next. Funciona como el incremento del for en C — ideal para mantener contadores o resetear estado entre líneas.

Dispatch table (switch)
# Hash de code refs (más Perl-ish que switch):
my %acciones = (
    nuevo   => sub { say "Crear nuevo" },
    abrir   => sub { say "Abrir fichero" },
    guardar => sub { say "Guardar" },
);

my $cmd = "abrir";
if (exists $acciones{$cmd}) {
    $acciones{$cmd}->();   # llama a la sub
} else {
    say "Comando desconocido";
}

Las dispatch tables (hash de code refs) son la alternativa idiomática al switch/case en Perl. Mapean valores a acciones de forma flexible y extensible. El given/when existe pero es experimental y desaconsejado.

Referências e Estruturas


10 cards
Crear referencias
my @arr = (1, 2, 3);
my %hash = (a => 1);
sub funcion { 42 }

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

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

Las referencias se crean con barra invertida: \@arr, \%hash, \&sub. Son escalares que "apuntan" a otra variable. ref() devuelve el tipo de la referencia (ARRAY, HASH, CODE).

Autovivificación
my $datos;   # undef

# Perl crea las estructuras intermedias:
$datos->{users}[0]{nombre} = "Ana";

# Ahora $datos es:
# { users => [ { nombre => "Ana" } ] }

# Cuidado con typos (crea claves sin error):
say $datos->{usres};   # undef, ¡sin aviso!

La autovivificación es la creación automática de estructuras intermedias al acceder — conveniente pero puede enmascarar bugs (los typos crean claves en vez de errores). Con use strict y cuidado, es una funcionalidad poderosa.

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

blessed($obj);              # clase o 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 "Es un array con " . scalar(@$x) . " elems";
}

Scalar::Util proporciona introspección de tipos: blessed devuelve la clase de un objeto, reftype el tipo de la referencia, looks_like_number valida números. Útil para código defensivo antes de desreferenciar.

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

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

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

# Anidados:
my $datos = {
    users => [ { nombre => "Ana" }, { nombre => "Ray" } ],
};

[] crea una referencia anónima a array y {} a hash (sin necesitar variable nombrada). Son la base de las estructuras de datos complejas en Perl — equivalentes a arrays y objetos literales de otros lenguajes.

Array de hashes
# Lista de registros (patrón muy común):
my @empleados = (
    { nombre => "Ana", dept => "TI", salario => 2500 },
    { nombre => "Ray", dept => "RRHH", salario => 2000 },
);

# Acceso:
say $empleados[0]{nombre};   # Ana
$empleados[1]{salario} += 200;

# Iterar:
for my $e (@empleados) {
    say "$e->{nombre}: $e->{dept}";
}

El array de hashes es el patrón para listas de registros (como resultados de queries SQL). Cada elemento es una referencia a hash. El acceso combina índice y clave: $empleados[0]{nombre}.

Árboles (estructura recursiva)
# Árbol recursivo:
my $arbol = {
    valor => "root",
    hijos => [
        { valor => "A", hijos => [] },
        { valor => "B", hijos => [
            { valor => "B1", hijos => [] },
        ]},
    ],
};

# Recorrer (recursivo):
sub imprimir {
    my ($nodo, $nivel) = @_;
    say "  " x $nivel . $nodo->{valor};
    imprimir($_, $nivel + 1) for @{$nodo->{hijos}};
}
imprimir($arbol, 0);

Las estructuras recursivas como árboles se construyen con hashes y arrays anidados. El recorrido es naturalmente recursivo. " " x $nivel indenta según la profundidad. Data::Dumper ayuda a inspeccionar estas estructuras.

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

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

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

# Sin flecha (sintaxis antigua):
my $elem2 = $$arr[0];

Se desreferencia con @/%/$ antes de la referencia, o con la flecha -> para elementos: $arr->[0], $hash->{a}, $sub->(). La flecha es la forma más legible y recomendada.

Deep copy
use Storable qw(dclone);

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

# Copia superficial (comparte refs):
my $superficial = $original;

# Copia profunda (independiente):
my $copia = dclone($original);

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

Asignar una referencia copia el puntero, no los datos (los cambios afectan a ambos). Storable::dclone hace una copia profunda e independiente — esencial cuando necesitas modificar una estructura sin alterar la original.

Arrow en cadena
my $datos = {
    personas => [
        { nombre => "Ana", ciudad => "Lisbon" },
        { nombre => "Ray", ciudad => "Porto" },
    ],
};

# Acceso encadenado:
my $ciudad = $datos->{personas}[0]{ciudad};
say $ciudad;   # Lisbon

# La flecha entre ] y { es opcional:
$datos->{personas}->[0]->{ciudad};   # igual

En accesos encadenados como $datos->{personas}[0]{ciudad}, la flecha entre ] y { es opcional — Perl infiere el tipo automáticamente. Esto hace el acceso a estructuras complejas conciso y legible.

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

my $a = { nombre => "padre" };
my $b = { nombre => "hijo" };

$a->{hijo} = $b;
$b->{padre} = $a;   # ¡ciclo de referencia!

weaken($b->{padre});   # no cuenta para el GC

# Ahora $b->{padre} no impide que $a sea liberado

Las referencias débiles (weaken) no cuentan para el garbage collector — esenciales en estructuras circulares. Sin weaken, los ciclos de referencia nunca se liberan (memory leak), pues el reference counting de Perl no los detecta.

Expressões Regulares


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

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

# Negación !~:
if ($texto !~ /xyz/) {
    say "No encontrado";
}

# En contexto booleano implícito ($_):
if (/patron/) { say "en $_" }

El operador =~ aplica un patrón regex a una string y devuelve verdadero si lo encuentra. !~ niega. Sin =~, el match opera sobre $_ (la variable por defecto). Es la base de todo el procesamiento de texto en Perl.

Captura con paréntesis
my $fecha = "2024-01-15";

if ($fecha =~ /(\d{4})-(\d{2})-(\d{2})/) {
    my ($anio, $mes, $dia) = ($1, $2, $3);
    say "Año: $anio, Mes: $mes, Día: $dia";
}

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

Los paréntesis capturan grupos en las variables $1, $2, $3... En contexto de lista, el match devuelve todas las capturas de una vez. Es la forma principal de extraer partes de una string.

Transliteración tr///
my $texto = "Hola Mundo";

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

# Eliminar vocales (/d delete):
$texto =~ tr/aeiou//d;

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

# tr/// NO es regex:
# opera carácter a carácter

tr/// translitera carácter a carácter (no es regex). tr/a-z/A-Z/ convierte a mayúsculas, /d elimina los caracteres listados. En contexto escalar devuelve el número de caracteres sustituidos — útil para contar.

Metacaracteres
/^inicio/      # inicio de línea
/fin$/         # fin de línea
/a.c/          # cualquier char entre a y c
/colou?r/      # u opcional (color/colour)
/ab+c/         # b una o más veces
/ab*c/         # b cero o más veces
/a{2,4}/       # a entre 2 y 4 veces
/\d+/          # uno o más dígitos

Los metacaracteres definen patrones: ^ inicio, $ fin, . cualquier carácter, ? opcional, + una o más, * cero o más, {n,m} cantidad. \d es dígito. Cubren el 90% de los patrones comunes.

Capturas nombradas
my $fecha = "2024-01-15";

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

# Más legible que $1, $2, $3 en regex complejas
# %+ guarda las capturas nombradas

Las capturas nombradas (?<nombre>...) guardan el resultado en %+ (accedido con $+{nombre}). Son mucho más legibles que $1, $2 en regex complejas, pues el nombre documenta el significado de cada grupo.

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

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

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

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

# No consumen caracteres

El lookahead (?=) y el lookbehind (?<=) verifican el contexto sin consumir caracteres — validan formato sin capturar el delimitador. Las versiones negativas (?! y (?<! verifican ausencia. Esenciales para validación precisa.

Clases de caracteres
/\d/       # dígito [0-9]
/\w/       # word char [a-zA-Z0-9_]
/\s/       # whitespace (espacio, tab, \n)
/\D/       # no-dígito
/\W/       # no-word char
/\S/       # no-whitespace

/[aeiou]/  # una vocal
/[^0-9]/   # cualquier no-dígito
/[a-z]+/   # letras minúsculas

# El punto no matchea \n por defecto:
/./s       # con /s, matchea \n también

Las clases [] definen conjuntos de caracteres aceptados; [^...] niega. Atajos: \d dígito, \w word char, \s whitespace (y las mayúsculas niegan). El . matchea cualquier carácter excepto nueva línea (a menos que uses /s).

Sustitución s///
my $texto = "Hola Mundo";

# Sustitución simple:
$texto =~ s/Mundo/Perl/;   # "Hola Perl"

# Global (todas las ocurrencias):
$texto =~ s/o/0/g;

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

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

s/// sustituye texto: s/viejo/nuevo/. El modificador /g sustituye todas las ocurrencias. Los backreferences $1, $2 reutilizan grupos capturados en el replacement. Es la herramienta central de transformación de texto.

qr// y patrones reutilizables
# qr// compila la regex en un objeto:
my $fecha_re = qr/\d{4}-\d{2}-\d{2}/;

if ($texto =~ $fecha_re) {
    say "Tiene una fecha";
}

# Composición de patrones:
my $num = qr/\d+/;
my $sep = qr/[\/-]/;
my $fecha2 = qr/$num$sep$num$sep$num/;

# Interpola en otras regex

qr// compila una regex en un objeto reutilizable e interpolable. Permite construir patrones complejos por composición (qr/$num$sep$num/) y compartir regex entre varias operaciones. Acepta los mismos modificadores.

Modificadores
/patron/i    # case-insensitive
/patron/g    # global (todas las ocurrencias)
/patron/m    # multiline (^ $ por línea)
/patron/s    # . matchea \n también
/patron/x    # permite espacios y comentarios

# Ejemplo /x (regex legible):
my $re = qr/
    ^ \d{4}   # año
    - \d{2}   # mes
    - \d{2}   # día
$/x;

Los modificadores alteran el comportamiento: /i ignora mayúsculas, /g encuentra todas las ocurrencias, /m hace que ^/$ actúen por línea, /x permite espacios y comentarios para hacer regex legibles. Se combinan: /gix.

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

# Uppercase de cada palabra:
my $t = "hola mundo";
$t =~ s/(\w+)/ucfirst($1)/ge;
# "Hola Mundo"

# Sin /e, $1*2 sería literal

El modificador /e evalúa el replacement como código Perl en vez de string — $1 * 2 calcula el doble en vez de quedar literal. Combinado con /g, aplica la transformación a cada match. Ideal para transformaciones dinámicas.

split con regex y \K
# split con patrón flexible:
my @campos = split(/\s*,\s*/, "a, b ,c");
# ("a", "b", "c")

# \K (reset del inicio del match):
my $s = "foobar";
$s =~ s/foo\Kbar/baz/;   # "foobaz"
# sustituye "bar" pero mantiene "foo"

# Equivalente a: s/(foo)bar/${1}baz/
# pero \K es más simple

split acepta regex para separadores flexibles (\s*,\s* ignora espacios). \K "olvida" lo que matcheó antes — en s/foo\Kbar/baz/ sustituye solo bar manteniendo foo, simplificando el uso de backreferences.

OO e Módulos


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

sub new {
    my ($clase, %args) = @_;
    return bless {
        nombre => $args{nombre},
        sonido => $args{sonido} // "...",
    }, $clase;
}

sub hablar {
    my $self = shift;
    return "$self->{nombre}: $self->{sonido}!";
}

1;   # los módulos DEBEN terminar con true

Un módulo es un fichero .pm con un package correspondiente a la ruta. bless asocia una referencia hash a una clase, creando un objeto. El constructor new es convención. El 1; al final es obligatorio — use verifica que el módulo retornó verdadero.

Moose (OO completo)
package Cuenta;
use Moose;

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

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

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

__PACKAGE__->meta->make_immutable;

Moose es el sistema OO más completo: isa => 'Num' valida tipos automáticamente, make_immutable optimiza la clase. La penalización es el tiempo de carga — por eso Moo existe como alternativa ligera. Inspiró el OO moderno en Perl.

AUTOLOAD
package Config;
our $AUTOLOAD;

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

my $cfg = bless {}, 'Config';
$cfg->nombre("Ana");   # crea dinámicamente
say $cfg->nombre;      # "Ana"

AUTOLOAD se llama cuando un método no existe — permite crear getters/setters dinámicos. La variable $AUTOLOAD contiene el nombre del método llamado. Es poderoso pero dificulta el debugging; Moo/Moose generan accessors estáticamente.

Crear y usar objetos
use Animal;

# Crear objeto:
my $a = Animal->new(nombre => "Rex", sonido => "Guau");

# Llamar métodos (flecha ->):
say $a->hablar;   # "Rex: Guau!"

# $self es el objeto (1er argumento):
sub hablar {
    my $self = shift;   # saca el objeto
    return $self->{nombre};
}

Los objetos se crean con Clase->new(...) y los métodos se llaman con la flecha ->. El primer argumento de todo método es el propio objeto ($self), sacado con shift. Los datos viven en el hash $self->{campo}.

Roles (composición)
# Role (comportamiento reutilizable):
package Serializable;
use Moo::Role;

requires 'to_hash';   # método obligatorio

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

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

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

Los roles son composición de comportamiento — la alternativa moderna a la herencia múltiple. requires declara métodos que el consumidor debe implementar; with aplica el role. Evitan el "diamond problem" de la herencia múltiple.

DESTROY
package Conexion;

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

# Destructor (llamado en el garbage collection):
sub DESTROY {
    my $self = shift;
    $self->{dbh}->disconnect if $self->{dbh};
    say "Conexión cerrada";
}

# Si usas AUTOLOAD, define un DESTROY vacío:
sub DESTROY { }   # impide AUTOLOAD

DESTROY es el destructor — llamado automáticamente cuando el objeto es garbage collected. Esencial para cerrar conexiones y liberar recursos. Si usas AUTOLOAD, define siempre un DESTROY (aunque sea vacío) para interceptarlo.

Herencia
package Perro;
use parent 'Animal';   # hereda de Animal

sub hablar { "Guau guau" }   # override

package PerroGuia;
use parent 'Perro';

sub hablar {
    my $self = shift;
    my $base = $self->SUPER::hablar();   # llama al padre
    return "$base (perro guía)";
}

my $p = Perro->new(nombre => "Rex");
say $p->isa('Animal');   # 1

use parent configura la herencia (la lista @ISA). SUPER:: llama al método de la clase padre. isa() verifica si un objeto es de una clase (o derivada). Perl soporta herencia múltiple: use parent 'A', 'B'.

Encapsulamiento
package Banco;
use Moo;

# setter privado (rwp):
has saldo => (
    is       => 'rwp',     # read-write privado
    init_arg => undef,     # no en el constructor
    default  => 0,
);

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

# _set_saldo solo es accesible dentro de la clase

Perl no tiene private nativo, pero Moo/Moose ofrecen is => 'rwp' (setter privado _set_saldo) e init_arg => undef (excluye del constructor). La convención _prefijo indica "no tocar" los datos internos.

Signatures (Perl 5.36+)
use v5.36;

# Las signatures hacen destructuring automático:
sub suma ($a, $b) {
    return $a + $b;
}

# Con defaults y opcionales:
sub saludar ($nombre, $prefijo = "Hola") {
    return "$prefijo, $nombre!";
}

# Verifica la aridad automáticamente:
suma(1, 2);      # OK
suma(1);         # ERROR (faltan args)

Las signatures (Perl 5.36+) son la forma moderna de declarar parámetros: sub suma ($a, $b) hace destructuring de @_ automáticamente y verifica la aridad. Sustituyen los prototipos antiguos y el my ($a, $b) = @_ manual.

Moo (OO moderno ligero)
package Persona;
use Moo;

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

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

# Uso:
my $p = Persona->new(nombre => "Ana", edad => 30);
say $p->nombre;      # Ana (getter)
$p->edad(31);        # setter (rw)

Moo es OO moderno minimalista. has declara atributos con accessors automáticos: is => 'ro' crea solo getter, 'rw' crea getter y setter. required => 1 lo hace obligatorio; default define el valor por defecto. Es compatible con Moose.

Sobrecarga de operadores
package Vector;
use overload
    '+'  => sub {
        my ($self, $otro) = @_;
        Vector->new($self->{x} + $otro->{x},
                    $self->{y} + $otro->{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 hace que los objetos se comporten como valores nativos: + suma, "" convierte a string (usado en say). fallback => 1 permite derivar operadores no definidos. Esencial para clases matemáticas.

Métodos de clase vs objeto
package Contador;
my $total = 0;   # variable de clase

sub new { bless {}, shift }

# Método de clase (llamado en la clase):
sub total_general {
    return $total;
}

# Método de objeto (llamado en la instancia):
sub incrementar {
    my $self = shift;
    $total++;
}

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

En Perl, los métodos de clase y de objeto usan la misma sintaxis ->. La diferencia está en el primer argumento: en la clase llega el nombre de la clase, en la instancia llega el objeto. shift lo saca en ambos casos.

Ficheiros e I/O


10 cards
open para lectura
# Three-argument form (seguro):
open(my $fh, "<", "fichero.txt")
    or die "No pude abrir: $!";

# Leer todo:
my @lineas = <$fh>;

# O línea a línea:
while (my $linea = <$fh>) {
    chomp $linea;
    say $linea;
}

close $fh;

open con tres argumentos (open $fh, MODO, FICHERO) es el patrón seguro — evita inyección de nombres de fichero. < es lectura. or die $! termina con el mensaje de error del sistema si falla. Cierra siempre con close.

Operador diamante
# Lee de los ficheros en @ARGV o STDIN:
while (my $linea = <>) {
    chomp $linea;
    print "$.: $linea\n";   # $. = nº de línea
}

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

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

El operador diamante <> lee líneas de los ficheros pasados en @ARGV o del STDIN — la base de los filtros de texto Unix. $. es el número de la línea actual. Con -pi -e hace edición in-place de ficheros.

Directorios
use File::Path qw(make_path remove_tree);
use File::Copy qw(copy move);

# Crear carpeta (recursivo):
make_path("datos/logs/2024");

# Listar contenido:
opendir(my $dh, "datos") or die $!;
my @ficheros = grep { !/^\./ } readdir($dh);
closedir $dh;

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

File::Path crea y elimina carpetas recursivamente (make_path). opendir/readdir listan el contenido (filtra . y .. con grep). File::Copy copia y mueve ficheros. Son módulos core de Perl.

open para escritura y append
# Escritura (trunca el fichero):
open(my $out, ">", "salida.txt")
    or die "No pude crear: $!";
print $out "Linea $_\n" for 1..10;
close $out;

# Append (añade al final):
open(my $log, ">>", "app.log")
    or die "Error: $!";
say $log "[" . localtime . "] evento";
close $log;

> abre para escritura (borra el contenido existente); >> abre para append (añade al final). print $out y say $log escriben en el filehandle. Usa siempre or die para tratar fallos de apertura.

Variables especiales de I/O
$!    # error del sistema (errno)
$.    # número de línea del último handle
$_    # variable por defecto
$/    # separador de entrada (default \n)
$\    # separador de salida
$|    # autoflush (1 = sin buffer)

# Leer párrafos:
local $/ = "";        # modo párrafo
while (my $parrafo = <$fh>) { ... }

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

Perl tiene variables especiales de I/O: $! es el error del sistema, $. el número de línea, $/ el separador de entrada. Cambiar $/ a "" lee por párrafos; a undef lee el fichero entero (slurp).

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

# Recorrer el árbol de carpetas:
find(\&procesar, "datos");

sub procesar {
    return unless -f $_;        # solo ficheros
    return unless /\.pm$/;      # solo .pm
    say "$File::Find::name";    # ruta completa
}

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

File::Find recorre recursivamente un árbol de directorios, llamando a una sub por cada entrada. $_ es el nombre y $File::Find::name la ruta completa. Los operadores -f (fichero) y -s (tamaño) filtran los resultados.

Encoding UTF-8
# Lectura con encoding:
open(my $fh, "<:encoding(UTF-8)", "f.txt")
    or die "Error: $!";

# Escritura con encoding:
open(my $out, ">:encoding(UTF-8)", "o.txt")
    or die "Error: $!";

# Pragmas para todo el script:
use open ':std', ':encoding(UTF-8)';
use utf8;   # código fuente en UTF-8

La capa :encoding(UTF-8) garantiza lectura/escritura correcta de Unicode. Sin ella, los caracteres acentuados pueden corromperse. use utf8 declara que el código fuente está en UTF-8; use open aplica encoding a todos los handles.

STDIN, STDOUT, STDERR
# Leer del teclado:
print "Nombre: ";
my $nombre = <STDIN>;
chomp $nombre;

# Escribir en el error:
say STDERR "¡Atención!";

# Redirigir STDOUT:
open(my $guardado, ">&", \*STDOUT);
open(STDOUT, ">", "log.txt");
say "va al fichero";
open(STDOUT, ">&", $guardado);   # restaurar

STDIN, STDOUT y STDERR son los filehandles estándar (entrada, salida, error). say STDERR escribe en el error sin afectar el output normal. Pueden redirigirse a ficheros con open.

Path::Tiny
use Path::Tiny;

# Leer el fichero completo:
my $contenido = path("f.txt")->slurp_utf8;

# Escribir:
path("out.txt")->spew_utf8($contenido);

# Líneas:
my @lineas = path("f.txt")->lines_utf8;

# Otras operaciones:
path("datos")->mkpath;          # crear carpeta
path("f.txt")->exists;          # ¿existe?
path("f.txt")->basename;        # "f.txt"

Path::Tiny (CPAN) moderniza el I/O de ficheros: slurp_utf8 lee todo, spew_utf8 escribe, con encoding automático. Es mucho más conciso que open/close manual y trata los errores de forma predecible.

die y errno
# Patrón idiomático de error:
open(my $fh, "<", $file)
    or die "No pude abrir '$file': $!";

# $! contiene el mensaje del sistema:
# "No such file or directory"

# die con código de salida:
die "Error fatal\n";   # exit code != 0

# warn (no termina):
warn "Aviso: $!" if $problema;

or die es el patrón de tratamiento de errores en Perl: intenta la operación o termina con mensaje. $! contiene la descripción del error del sistema. die termina el programa; warn solo imprime un aviso y continúa.

Dicas e Boas Práticas


8 cards
strict y warnings siempre
# Siempre al inicio de cada script/módulo:
use strict;
use warnings;

# O moderno (activa ambos):
use v5.36;

# strict: los typos se vuelven errores de compilación
# warnings: operaciones sospechosas generan avisos

# Sin ellos, bugs silenciosos:
$nombr = "Ana";   # ¡typo! variable global

use strict y use warnings son obligatorios en Perl moderno. strict transforma typos en variables en errores de compilación; warnings alerta sobre operaciones sospechosas. use v5.36 los activa ambos automáticamente.

Convenciones de nomenclatura
$escalar    # variable escalar
@array      # array
%hash       # hash
&sub        # subrutina (raro)

# Nombres:
my $contador_total;     # snake_case
sub calcular_descuento; # snake_case
package Mi::Clase;      # PascalCase

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

Convenciones Perl: variables y subs en snake_case, packages en PascalCase (con :: como separador). El sigilo ($, @, %) indica el tipo. use constant crea constantes. Prefiere nombres descriptivos.

// vs ||
my $puerto = 0;

# || trata 0 y "" como falsos (PELIGRO):
my $a = $puerto || 3306;   # 3306 (¡incorrecto!)

# // solo usa el default si undef (SEGURO):
my $b = $puerto // 3306;   # 0 (¡correcto!)

# Regla: usa // para valores que pueden
# legítimamente ser 0 o string vacía

La distinción || vs // es crítica: || trata 0, "" y "0" como falsos (devuelve el default erróneamente), mientras que // solo se activa con undef. Usa // para valores que pueden legítimamente ser cero o string vacía.

Módulos esenciales
# Siempre útiles (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;       # excepciones
use JSON;            # JSON
use DateTime;        # fechas

Estos módulos resuelven el 90% de las tareas comunes: List::Util y Scalar::Util (core) para colecciones y tipos; Path::Tiny, Try::Tiny, JSON y DateTime (CPAN) modernizan I/O, excepciones, serialización y fechas.

$_ la variable default
# $_ se usa cuando no se especifica nada:
for (@lista) { say }        # say $_
print if /patron/;          # $_ =~ /patron/
my $x = length;             # length($_)

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

# Cuidado: $_ es global y compartida
# En código complejo, usa variables nombradas

$_ es la variable default de Perl — usada por print, say, map, grep, regex y muchas funciones cuando no se da ningún argumento. Hace el código conciso, pero en exceso reduce la claridad. Úsala con moderación.

Debugging
# Inspeccionar variables:
use Data::Dumper;
warn Dumper($estructura);

# warn no termina (bueno para debug):
warn "Aquí: $valor\n";

# Depurador interactivo:
# perl -d script.pl

# Comandos del depurador:
# s (step), n (next), p $var (print)
# b 10 (breakpoint línea 10), c (continue)

Data::Dumper + warn es la forma rápida de inspeccionar variables sin parar el programa. Para debugging serio, perl -d abre un depurador interactivo con breakpoints (b), step (s) e inspección (p).

One-liners
# Sustituir en ficheros (-pi -e):
perl -pi -e 's/foo/bar/g' *.txt

# Imprimir líneas que matchean:
perl -ne 'print if /error/' log.txt

# Sumar la 2ª columna:
perl -lane '$s += $F[1]; END { say $s }' datos

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

Los one-liners Perl son poderosos para procesamiento de texto: -n envuelve en while(<>), -p añade print, -l hace chomp y nueva línea, -a divide en @F, -i edita in-place. Sustituyen awk/sed en muchas tareas.

Buenas prácticas generales
# 1. strict + warnings siempre
use v5.36;

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

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

# 4. Nombres descriptivos, no $_ en exceso
for my $cliente (@clientes) { ... }

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

Buenas prácticas Perl: activa strict/warnings, usa // para defaults, open con tres argumentos y or die, nombres descriptivos en vez de $_ en exceso, y módulos del CPAN en vez de reinventar la rueda. El código Perl debe ser legible, no "mágico".