El paquete que desarrollaremos en la práctica 6.8.5 permite trabajar cómodamente con números fraccionarios. Sin embargo, la forma de crearlos implica el uso explícito del constructor:
my $d = fraction->new(4,5);Mientras que para un número podemos escribir directamente
$d = 4
,
ya que Perl es capaz de deducir el tipo. Sería bueno escribir nuestra
práctica de manera que
constantes de tipo cadena o numéricas
conteniendo fracciones fueran convertidas en objetos de tipo fracción.
Para cambiar el modo en que Perl interpreta las constantes enteras, flotantes,
cadenas y expresiones regulares podemos crear un conjunto de ''manejadores''
mediante la subrutina overload::constant
.
Se espera que dicho manejador devuelve un valor escalar que es usado en lugar de
la interpretación normal. Por ejemplo, para usar overload::constant
en
el paquete Math::BigFloat
para modificar el modo en que las constantes enteras
y flotantes se interpretan en Perl haríamos:
package Math::BigFloat; use Math::BigInt; use overload; my %_constant_handlers = ( integer => sub { return Math::BigInt->new($_[0]) }, float => sub { return Math::BigFloat->new($_[0]) } ); sub import { overload::constant %_constant_handlers } sub unimport { overload::remove_constant %_constant_handlers }Obsérve el uso de
import
(véase sección 5.7) que como
sabemos es ejecutada cada vez que se usa (use Math::BigFloat
) el módulo.
Aquí la función de import
no es exportar símbolos sino hacer
que se ejecute la llamada overload::constant %_constant_handlers
produciendo la consiguiente modificación de la interpretación de las constantes
enteras y flotantes.
La subrutina overload::constant
toma como argumento un hash y espera que las entradas tengan
una de las siguientes claves:
integer
, indicando el manipulador para enteros decimales,
float
, indicando el manejador de números en punto flotante,
binary
, indicando el manipulador de constantes octales y hexadecimales,
q
, indicando el manipulador para las cadenas constantes (esto es,
las cadenas '...'
, "..."
, q{...}
, y qq{...}
,
los argumentos de tr/.../.../
, o el segundo argumento de una sustitución
s/.../.../
,
qr
, indicando el manipulador de una expresión regular (por ejemplo,
el primer argumento de m/.../
o de s/.../.../
).
El correspondiente valor para cada clave debe ser una referencia a una subrutina. La subrutina es responsable de proporcionar un valor final al tipo particular de constante que esta siendo interpretado. Se le pasan tres parámetros:
q
y qr
. Para esos manipuladores toma uno de los siguientes valores:
q
, indicando que la cadena viene de un contexto no interpolable como '...'
o q{...}
o qw{...}
,
qq
, indicando que la cadena aparece en un contexto interpolable como "..."
o qq{...}
o m/.../
o el primer argumento de una sustitución s/.../.../
,
tr
, indicando que la cadena aparece en tr/.../.../
o y/.../.../
,
s
, indicando que la cadena es el segundo argumento de una sustitución s/.../.../
.
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 2La 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.8.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.