Aprendiendo Perl, el reto: Encontrar duplicados

Marcelo marcelo.magallon en gmail.com
Mie Abr 4 19:40:18 PDT 2012


¡Hola!

2012/4/3 Javier Tiá <javier.tia at gmail.com>:

> sub get_hash($)

Esta es una mala costumbre mía que me ha costado mucho soltar.  Los
prototipos para las subrutinas en Perl pueden ser contraproducentes.
En este caso el efecto del prototipo es que Perl reclama si uno no
llama a la subrutina con exactamente un argumento, y es la razón por
la que a mi me cuesta deshacerme de esa costumbre.

Sin embargo en este caso:

sub magia(\@\@)

tiene un efecto inesperado.  Si uno invoca a esta función así:

magia(@a, @b);

cualquier desarrollador de Perl viendo esa línea simplemente asumirá
que @a y @b se van a "juntar" en un solo gran array en @_ dentro de la
subrutina, pero por el prototipo lo que Perl realmente hace es pasar
los dos arrays como referencias.  Uno puede argumentar que eso es
bueno porque es exactamente lo que quien escribió el código espera que
suceda, pero no es evidente para quien está leyendo el código.  Esto
en cambio es claro:

magia(\@a, \@b);

Hay otra razón para no usar prototipos: no hacen lo que la mayoría
queremos que hagan.  Lo que un prototipo realmente hace es imponer
contexto, no requerir una cantidad particular de argumentos como
señalé antes.  Intuitivamente uno espera que Perl reclame con este
programa:

#!/usr/bin/env perl

use Modern::Perl;

sub n($)
{
    my $arg = shift;
    say $arg;
}

my $a = 1;
my @b = (0, 0, 0);

n($a);
n(@b);

pero no, este programa no solo corre sin objecciones sino que hace
exactamente lo dice ahí que tiene que hacer.  Si reclama en este caso
por ejemplo:

n($a, @b);

y reclama exactamente en la forma que uno quiere que reclame:

Too many arguments for main::n at [...], near "@b)"

por eso cuesta tanto abandonarlos.

> {
>    open(FILE, $_);
>    return Digest::MD5->new->addfile(*FILE)->hexdigest;
> }

En general uno no debería usar otra cosa que no sea la forma de tres
argumentos de open, así:

open(FILE, "<", $_)

Otra vez: esto hace explícito que es lo que uno pretende.

La otra cosa es que uno no debería usar "palabras chingas" (barewords)
como "FILE".  En el caso de este código en particular, lo que está
sucediendo es que se abre el archivo, se lee y se deja abierto hasta
la siguiente llamada de esa función.  Hasta ese momento se cierra el
archivo porque Perl ocupa el filehandle (FILE).  Se abre el siguiente
archivo y queda abierto hasta la siguiente vez que se llame esa
función.  Esto sucede porque el "scope" de *FILE es el paquete, no la
función.  Y el problema con los barewords es exactamente ese.

Y una cosa que tal vez es menos cosmética es el uso de $_.  $_ *no* es
el argumento de la función.

Si uno va a:

   $hashes{$File::Find::name} = get_hash($_) if -f $_;

y cambia "get_hash($_)" por "get_hash('el cielo pintado de azul')" se
ve que el código funciona exactamente igual.  En la función $_ sigue
siendo la misma variable que en el entorno donde fue llamada.  No solo
tiene el mismo valor, es la misma variable.

Este programa puede resultar sorpresivo:

#!/usr/bin/env perl

use Modern::Perl;

sub n { $_ = 2; }

$_ = 1;
n;
say;

Saludos,

Marcelo


Más información sobre la lista de distribución Costarica-pm