Búsqueda de la Implementación de un Operador

Búsqueda del Manejador de un Operador Sobrecargado

Normalmente la implementación de un operador sobrecargado se busca en el siguiente orden:

  1. Si el operador ha sido explícitamente sobrecargado se llama a la correspondiente subrutina
  2. Si no, se mira a ver si el mecanismo de propagación automático descrito en la página [*] puede ser aplicado
  3. En otro caso, se mira a ver si el pseudo operador nomethod fué definido. Si es así se llama a la subrutina asociada con nomethod.
  4. En cualquier otro caso generar una excepción

Búsqueda del Manejador con fallback Cierto

Este orden puede alterarse si se define el pseudo operador fallback.

Si fallback esta definido y es cierto, la secuencia es la misma que en el caso anterior, excepto que, en vez de generar una excepción, la operación pasa a implementarse siguiendo la conducta normal de Perl para ese operador:

  1. Si el operador ha sido explícitamente sobrecargado se llama a la correspondiente subrutina
  2. Si no, se mira a ver si el mecanismo de propagación automático descrito en la página [*] puede ser aplicado
  3. En otro caso, se mira a ver si el pseudo operador nomethod fué definido. Si es así se llama a la subrutina asociada con nomethod
  4. Aplicar la conducta normal de Perl para ese operador

Búsqueda del Manejador con fallback Falso

Si fallback esta definido pero es falso, el mecanismo de propagación de la sobrecarga (anterior paso 2) es eliminado y la secuencia es:

  1. Si el operador ha sido explícitamente sobrecargado se llama a la correspondiente subrutina
  2. En otro caso, se mira a ver si el pseudo operador nomethod fué definido. Si es así se llama a la subrutina asociada con nomethod.
  3. En cualquier otro caso generar una excepción

Este último orden proporciona un mecanismo para aquellas situaciones en las que se quiere evitar el proceso de propagación automática de la sobrecarga. Por ejemplo, supongamos que queremos sobrecargar la operación de diferencia entre días de la semana (lunes, martes, ...) de manera que podamos determinar el número de días entre las mismos. Parece que no tendría sentido usar el operador de negación unaria. Para ello podemos asociar con el operador neg una referencia a una subrutina que provoque una excepción:

package DaysOfTheWeek;
use overload
  "-"         =>  \delta,
  "nomethod"  =>  sub { croak "No tiene sentido negar un dia de la semana\n" };

Sin embargo, este método es ineficiente si el número de operadores autogenerados cuyo uso se quiere prohibir es grande. Una mejor solución es:

package DaysOfTheWeek;
use overload
  "-"         =>  \delta,
  "fallback"  =>       0,
  "nomethod"  =>  sub { croak "No tiene sentido $_[3]\n" };
El cuarto argumento de nomethod es el operador ''realmente'' solicitado ($_[3])

Argumentos de nomethod

Los argumentos que se pasan a la subrutina asociada con nomethod son:

  1. El primer operando
  2. El segundo operando (undef si no existe)
  3. Un flag indicando cuando los operandos fueron intercambiados
  4. El operador ''realmente'' solicitado ($_[3])

Sobrecarga y Herencia

La sobrecarga puede combinarse con la herencia. Cualquier subclase heredará los métodos de la clase materna y puede sobreescribir cualquier operador a conveniencia.



Subsecciones
Casiano Rodríguez León
2010-03-03
>
  • tr, indicando que la cadena aparece en tr/.../.../ o y/.../.../,
  • s, indicando que la cadena es el segundo argumento de una sustitución s/.../.../.
  • Ejemplo: el Módulo Number::Fraction

    El módulo Number::Fraction permite la sobrecarga de constantes. Véase un ejemplo:

    lhp@nereida:~/Lperl/src$ cat -n fractions.pl
     1  #!/usr/bin/perl
     2  use warnings;
     3  use strict;
     4  use Number::Fraction ':constants';
     5
     6  no warnings 'numeric';
     7  my $trescuartos = '1/2'+'1/4';
     8  print "$trescuartos\n";
     9
    10  my $x = '1hola'+2;
    11  print "$x\n";
    12
    13  no Number::Fraction;
    14  $trescuartos = '1/2'+'1/4';
    15  print "$trescuartos\n";
    
    La salida de este programa es:
    $ ./fractions.pl
    3/4
    3
    2
    
    La transformación de las constantes ocurre en tiempo de compilación como muestran las siguientes ejecuciones con B::Terse6.1

    $ perl -MO=Terse,-exec \
           -MNumber::Fraction=':constants' \
           -e '$x = "1/2"'
    OP (0x81ce758) enter
    COP (0x824c708) nextstate
    SVOP (0x8290460) const [2] RV (0x8298d88) \HASH
    PADOP (0x824c308) gvsv  GV (0x814f648) *x
    BINOP (0x8235818) sassign
    LISTOP (0x8235048) leave [1]
    -e syntax OK
    
    $ perl -MO=Terse,-exec -e '$x = "1/2"'
    OP (0x81570b8) enter
    COP (0x816c5a0) nextstate
    SVOP (0x81571a8) const [2] PV (0x814f5dc) "1/2"
    PADOP (0x816c740) gvsv  GV (0x814f660) *x
    BINOP (0x816c880) sassign
    LISTOP (0x816c708) leave [1]
    -e syntax OK
    

    Esta sobrecarga se consigue definiendo import en la línea 22 del listado que sigue a continuación, para que llame a overload::constant sobre el hash %_const_handlers.

    En este caso el hash tiene una única clave, ya que sólo estamos interesados en tratar las constantes de tipo cadena. El constructor new será especificado en la sección 6.9.5: Cuando recibe la cadena produce el objeto Number::Fraction.

     1 package Number::Fraction;
     2 
     3 use 5.006;
     4 use strict;
     5 use warnings;
     6 use Carp;
     7 
     8 our $VERSION = sprintf "%d.%02d", '$Revision: 1.34 $ ' =~ /(\d+)\.(\d+)/;
     9 
    10 use overload
    11   q("") => 'to_string',
    12   '0+' => 'to_num',
    13   '+' => 'add',
    14   '*' => 'mult',
    15   '-' => 'subtract',
    16   '/' => 'div',
    17   fallback => 1;
    18 
    19 my %_const_handlers =
    20   (q => sub { return __PACKAGE__->new($_[0]) || $_[1] });
    21 
    22 sub import {
    23   overload::constant %_const_handlers if $_[1] and $_[1] eq ':constants';
    24 }
    

    El import proveído hace que, en el caso de que el constructor no devuelva un objeto válido, se devuelva el segundo argumento, que es el valor que Perl le da por defecto a la constante. El resultado es la conversión correcta de aquellas cadenas que pueden ser interpretadas como fracciones y que otras cadenas como '1hola' sean interpretadas de acuerdo a las tradiciones de Perl.

    Nótese la condición para la importación en la línea 23: if $_[1] and $_[1] eq ':constants'. Esto significa que para activar el manejador de constantes de tipo cadena el programa cliente ha de declarar:

    use Number::Fraction ':constants';
    

    esto es una buena idea: cambiar el modo en que funcionan las constantes es un cambio lo suficientemente trascendental como para sólo hacerlo bajo petición explícita del usuario.

    Seguimos:

    26 sub unimport {
    27   overload::remove_constant(q => undef);
    28 }
    29 
    30 sub new {
    31  ...
    32 }
    

    Recuerde que la función unimport se ejecutará cuando el usuario haga una llamada en su programa a no Number::Fraction. En este caso la negación produce el abandono del manejo de las constantes.



    Subsecciones
    Casiano Rodríguez León
    2011-02-09