next up previous contents index practicapracticaPP2moodleLHPmoodlepserratacpanmodulospauseperlgoogleetsiiullpcgull
Sig: Propagación de la Sobrecarga Sup: Programación Orientada a Objetos Ant: Práctica: Tie Escalar Err: Si hallas una errata ...

Sobrecarga de Operadores

Supongamos que queremos escribir un módulo que permite trabajar con números en punto flotante de tamaño arbitrario. Su uso sería algo así:
#!/usr/bin/perl -w
use strict;
use Math::BigFloat;

my $a = Math::BigFloat->new('123_456_789_123_456_789');
my $y = $a->copy()/1_000_000_000;

print "a = $a\n";
print "-a = ",-$a,"\n";
print "y = $y\n";
print "a+y = ",$a+$y,"\n";
cuya ejecución nos da:
$ ./bigfloat.pl
a = 123456789123456789
-a = -123456789123456789
y = 123456789.123456789
a+y = 123456789246913578.123456789
y queremos que el módulo, como ilustra el ejemplo, sobrecargue las operaciones binarias y unarias usuales así como el uso de las constantes. Los mecanismos para escribir un módulo como este los proporciona el módulo overload.pm debido a Ilya Zakharevich, el cual se incluye en la distribución estandard de Perl. Este módulo permite la sobrecarga de operadores. Para sobrecargar los operadores para una clase dada, hay que pasarle a la sentencia use una lista de pares operador, referencia a código:
package Math::BigFloat;

use overload "*"   => \&fmul,
             "+"   => "fadd",
             "neg" => sub { Math::BigInt->new($_[0]->fneg()) };
Cada pareja consiste de una clave, que especifica el operador a sobrecargar, y una referencia a una subrutina, que será invocada cuando se encuentre el operador. La clave neg corresponde al operador de negación unaria.

La clave puede ser cualquiera de las reseñadas en la tabla 7.8. La referencia a la subrutina puede ser

La subrutina de implementación es llamada cada vez que un objeto de la clase en cuestión (en el ejemplo la clase Math::BigFloat ) es un operando del operador correspondiente. Por ejemplo, si ejecutamos con el depurador el programa:

$ cat -n ./bigfloat2.pl
1  #!/usr/bin/perl -d
2  use strict;
3  use Math::BigFloat;
4
5  my $a = Math::BigFloat->new('123_456_789_123_456_789');
6  my $y = $a->copy()/1_000_000_000;
7
8  print "a+y = ",$a+$y,"\n";
observaremos como la subrutina asociada con el + es llamada:
$ ./bigfloat2.pl
Default die handler restored.

Loading DB routines from perl5db.pl version 1.07
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(./bigfloat2.pl:5):       my $a = Math::BigFloat->new('123_456_789_123_456_789');
  DB<1> n
main::(./bigfloat2.pl:6):       my $y = $a->copy()/1_000_000_000;
  DB<1>
main::(./bigfloat2.pl:8):       print "a+y = ",$a+$y,"\n";
  DB<1> s
Hemos pulsado s para entrar en la subrutina ...
Math::BigInt::CODE(0x82f9acc)(/usr/share/perl5/Math/BigInt.pm:50):
50:     '+'     =>      sub { $_[0]->copy()->badd($_[1]); },
  DB<1> p "@_"
123456789123456789 123456789.123456789
Observe los dos argumentos: los dos números grandes. Seguimos ...
  DB<2> n
Math::BigInt::CODE(0x830c9f0)(/usr/share/perl5/Math/BigInt.pm:110):
110:    '""' => sub { $_[0]->bstr(); },
esta es la conversión necesaria del resultado a una cadena para ser impresa, continuamos ...
  DB<2>
a+y = 123456789246913578.123456789
Debugged program terminated.  Use q to quit or R to restart,
  use O inhibit_exit to avoid stopping after program termination,
  h q, h R or h O to get additional info.

Si la sobrecarga fué especificada a través de una referencia a subrutina se usa una llamada como subrutina mientras que si se especificó como referencia simbólica se usa la sintáxis de método. Por ejemplo, si $a y $b son dos objetos Math::BigFloat, para las declaraciones

package Math::BigFloat;

use overload "*"   => \&fmul,
             "+"   => "fadd",
             "neg" => sub { Math::BigInt->new($_[0]->fneg()) };
tendríamos los siguientes ejemplos de traducciones:

$a*$b Math::BigFloat::fmul($a, $b, "")
$a+$b $a->fadd($b, "")
-$a (sub { Math::BigFloat->new($_[0]->fneg()) })->($a, undef, "")

La diferencia entre proporcionar una referencia a una subrutina o un nombre de método está relacionada con la herencia. Cuando se proporciona una referencia estamos asegurándonos de que se llama a la rutina con ese nombre, desactivando el mecanismo de búsqueda asociado con la herencia. Cuando proporcionamos un nombre de método, el operador sobrecargado va a llamar a ese método siguiendo los mecanismos de búsqueda asociados con la herencia (véase la sección 7.6).

Obsérvese que, en cualquier caso, la subrutina de implementación es llamada siempre con tres argumentos:

  1. El primer operando
  2. El segundo operando (undef si no existe)
  3. Un flag indicando cuando los operandos fueron intercambiados
La necesidad del flag proviene del requerimiento de que el primer argumento debe ser un objeto de la clase sobrecargada (en el ejemplo la clase Math::BigFloat). Si Perl detecta una expresión de la forma 4+$a la traduce por $a->fadd(4,1), donde el segundo argumento avisa de la inversión producida.

Es por esto que, para operaciones no conmutativas como la resta o la divisón, la función de implementación suele comenzar asi:

sub substract {
  my ($op1, $op2, $reversed) = @_;
  ($op1, $op2) = ($op2, $op1) if $reversed;
  if (UNIVERSAL::isa($op1, 'Math::BigFloat') { 
    ... # $op1 es un objeto
  }
  ...
}
Veamos otro ejemplo, en el que el objeto esta a la derecha y a la izquierda tenemos una constante:
$ cat ./bigfloat3.pl
#!/usr/bin/perl -d
use strict;
use Math::BigFloat;

my $a = Math::BigFloat->new('123_456_789_123_456_789');

print "123_456_789_123_456_789-a = ",'123_456_789_123_456_789'-$a,"\n";
Al ejecutar, tenemos:
$ ./bigfloat3.pl
Default die handler restored.

Loading DB routines from perl5db.pl version 1.07
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(./bigfloat3.pl:5):       my $a = Math::BigFloat->new('123_456_789_123_456_789');
  DB<1> n
main::(./bigfloat3.pl:7):       print "123_456_789_123_456_789-a = ",'123_456_789_123_456_789'-$a,"\n";
  DB<1> s
Math::BigInt::CODE(0x82f997c)(/usr/share/perl5/Math/BigInt.pm:47):
47:     '-'     =>      sub { my $c = $_[0]->copy; $_[2] ?
48:                        $c->bneg()->badd($_[1]) :
49:                        $c->bsub( $_[1]) },
  DB<1> p "@_"
123456789123456789 123_456_789_123_456_789 1
Vemos como el tercer argumento esta a 1 y como la subrutina anónima que trata el caso del menos binario convierte la expresión $a-$b en (-$b)+$a en el caso en que el objeto es el segundo término.

Continuamos la ejecución:

  DB<2> c
123_456_789_123_456_789-a = 0
Debugged program terminated.  Use q to quit or R to restart,
  use O inhibit_exit to avoid stopping after program termination,
  h q, h R or h O to get additional info.
Observe que, en general se espera que los operadores sobrecargados puedan manejar la operación de números y objetos de la clase como en 4+$a. Por tanto nuestro código debe manipular no solo la operación de objetos sino también la de objetos y números. El ejemplo anterior del método substract, a través del uso del método UNIVERSAL::isa (véase sección 7.6.1) muestra una forma de hacerlo.

Tabla 7.3: Operadores que pueden ser sobrecargados en Perl. neg es la negación unaria
Categoría Operadores / Claves
Aritmética + - * / % ** x . neg
Bit << >> & | ^ ~
Asignación += -= *= /= %= **= <<= >>= x= .= ++ --
Comparación < <= > >= == != <=> lt le gt ge eq ne cmp
Funciones atan cos sin exp abs log sqrt
Conversiones q("") 0+ bool
Seudo-operadores nomethod fallback =




Subsecciones
next up previous contents index practicapracticaPP2moodleLHPmoodlepserratacpanmodulospauseperlgoogleetsiiullpcgull
Sig: Propagación de la Sobrecarga Sup: Programación Orientada a Objetos Ant: Práctica: Tie Escalar Err: Si hallas una errata ...
Casiano Rodríguez León
2006-02-21