Creative Commons License
Excepto donde se indique otra cosa, todo el contenido de este lugar está bajo una licencia de Creative Commons.
Taquiones > perl > pbp > essential > Usar un hash con los parametros si hay más de tres

Usar un hash con los parametros si hay más de tres

Si una subrutina recibe más de tres parámetros (ó parece que puede ser así en el futuro) lo idóneo es empaquetarlos en un hash y enviarle una referencia (ojo con esto).

La función ejemplo sirve para rellenar un texto con una longitud dada, usando un caracter concreto y opcionalmente centrado sobre él. Para ello recibe como parámetros:

  • text: texto a centrar
  • cols: longitud final
  • centered: indicador de centrado
  • filler: carácter de relleno
    1 sub rellenar {
    2     my ($arg_ref) = @_;
    3 
    4     my $sobrante = $arg_ref->{cols} - length $arg_ref->{text};
    5     my $izquierda = $arg_ref->{centered} ? int($sobrante/2) : 0;
    6     my $derecha = $sobrante - $izquierda;
    7 
    8     return  $arg_ref->{filler} x $izquierda
    9             . $arg_ref->{text}
   10             . $arg_ref->{filler} x $derecha;
   11 }
   12 
   13 my $linea = rellenar( { text => $mytext, cols => 30, filler => '.' });

Por qué usar una referencia

Damian recomienda pasar una referencia y no un hash para que la comprobación de errores se realice en tiempo de compilación, y no en tiempo de ejecución.

Por ejemplo en el siguiente código:

    1 my $linea = rellenar( { text => $mytext, cols => 30..40, filler => '.' });

El error Odd number of elements in anonymous hash ... se producirá según Perl lee y compila el código, mientras que en el caso de usar un hash:

    1 sub rellenar {
    2     my  %args = @_;
    3 
    4 
    5 }
    6 
    7 my $linea = rellenar( text => $mytext, cols => 30..40, filler => '.' );

El error se producirá en la línea 2, cuando se copie @_ sobre el hash %args en forma de una excepción. Es menos intuitivo según él.

Ventajas de usar un hash

Por contra, utilizar un hash permite hacer un pequeño truco que a mí particularmente me gusta mucho, y es el asignar valores por defecto según se reciben los parámetros:

    1 our %defaults = ( cols => 80, filler => ' ', centered => 0 );
    2 
    3 sub rellenar {
    4     my  %args = ( %defaults, @_ );
    5 
    6 }

Así que, mientras recibamos un hash correcto, tenemos la seguridad de que existen unos valores predeterminados en los parámetros.

Ahora bien, ¿ y si queremos hacer lo mismo con una referencia en lugar de una copia ?

    1 our %defaults = ( cols => 80, filler => ' ', centered => 0 );
    2 
    3 sub rellenar {
    4     my  $arg_ref = shift;
    5     my  %args    = ref $arg_ref eq 'HASH' ? ( %defaults, %{ $arg_ref } )
    6                     : %defaults;
    7 
    8     # etc.
    9 }

Es decir, sólo tomamos los parámetros si son una referencia a un hash, en caso contrario usamos sólo los valores por defecto.

Si alguno de los parámetros es fijo

En el caso de que uno ó varios parámetros se envíen siempre es recomendable pasarlos primero, por posición, y añadir después el hash con el resto de ellos.

En el caso de la función ejemplo el texto a centrar es obligatorio siempre (otra cosa no tiene mucho sentido) por lo que podemos redefinirla de alguna de estas dos formas:

    1 sub rellenar {
    2     my  ($text, $arg_ref) = @_;
    3 
    4 }
    5 
    6 sub rellenar {
    7     my  $text   =   shift;
    8     my  %args   =   ( %defaults, @_ );
    9 
   10 }