enero 04, 2006

Por fin sobrecarga de operadores en Delphi

Dandole unos cuantos teclazos al BDS 2006 estuve probando la característica de la que tanto se presumia antes de liberarse que era la inclusión de sobrecarga de operadores. Aunque yo esperaba que fuera a nivel de clases parece que nos tendremos que conformar por ahora con su implementación en estructuras de tipo record.

Por ejemplo, para implementar como una estructura el manejo de números fraccionarios usabamos algo como esto:
 TFraccionario = record
numerador, denominador: integer;
end;

Ahora tenemos la posibilidad de agregar operadores que se encarguen de la transferencia de información asi como de las operaciones entre las estructuras, por ejemplo agregando el operador implicit :
 TFraccionario = record
numerador, denominador: integer;
class operator implicit( Value: TFraccionario): string;
end;

y su implementación:
 class operator TFraccionario.implicit(Value: 
TFraccionario): string;
begin
Result := IntToStr(Value.Numerador) + '/'+
IntToStr(Value.Denominador);
end;

Ahora podemos asignar una variable de tipo TFraccionario a una simple y sencilla cadena asi:
var
R1 : TFraccionario;
S: string;
begin
R1.Numerador := 2;
R1.Denominador := 6;
S := R1; //Aqui S valdria literalmente "2/6"

Y también a la inversa, podemos definir un comportamiento de operador para cuando queremos asignar una cadena a una variable de tipo TFraccionario
 class operator implicit( Value: string) : TFraccionario;

Y lo implementamos haciendo un parseo muy simple para la cadena, al fin de cuentas nadamas estamos ilustrando un ejemplo, no es la idea poner un parser completo sino mostrar el funcionamiento de los operadores
class operator TFraccionario.implicit(Value: string): 
TFraccionario;
var p : integer;
begin
Result.numerador := 0;
Result.denominador := 0;
p := Pos('/',Value);
if P = 0 then exit;
Result.numerador := StrToIntDef(Copy(Value,1,P-1),0);
Result.denominador := StrToIntDef(Copy(Value,P+1,
Length(Value)),0);
end;

Y una vez hecho esto ya podemos asignar cadenas a variables de tipo TFraccionario, de esta manera:
 var
R1 : TFraccionario;
begin
R1 := '2/6';

Pero, ¿como sumariamos 2 números fraccionarios usando directamente el operador +?, pues bien para eso pondríamos una definición de operador llamada add dentro de la misma estructura del registro, ahora la declaración del tipo completo sería:
 TFraccionario = record
numerador, denominador: integer;
class operator implicit( Value: TFraccionario): string;
class operator implicit( Value: string) : TFraccionario;
class operator add( v1, v2 : TFraccionario): TFraccionario;
end;

Y la implementación del operador (si mis maestros de la primaria no me engañaron) sería algo como esto:
class operator TFraccionario.add(v1, v2: TFraccionario): 
TFraccionario;
begin
if (V1.denominador = V2.denominador) then
begin
Result.denominador := V1.denominador;
Result.numerador := V1.numerador + V2.numerador;
end
else
begin
Result.numerador := (V1.numerador * V2.denominador) +
(V1.denominador * V2.numerador );
Result.denominador := V1.denominador * V2.denominador;
end;
end;

Si, si, ya se que tambien se podría simplificar el número a su mínima expresión una vez efectuada la suma, pero prefiero dejarlo así por ahora para que no se complique en demasía el algoritmo y podamos admirar el trabajo con los operadores en vez de eso.

Ahora a poner a prueba el trabajo que hicimos, y a hacer también porque no algunos experimentos, primero veamos como podemos inicializar dos números fraccionarios a partir de un par de cadenas y sumemos ambos números y mostremos el resultado de la operación también en una cadena, para hacer uso de todos los operadores que hicimos, para eso ponemos un TButton en el formulario y le codificamos esto en el evento OnClick:
procedure TForm2.Button1Click(Sender: TObject);
var
R1,R2 : TFraccionario;
begin
R1 := '2/6';
R2 := '3/6';
Caption := R1 + R2; //Esto devuelve "5/6" en el caption
end;

¡Bastante práctico!, ¿no es cierto?... ¿pero ahora si en vez de que se aplicara la operación entre ambos números fraccionarios lo que nos interesara fuera obtener la concatenación de sus representaciones como cadenas???, bien en ese caso lo que hay que hacer es tratar a ambas variables como cadenas para que el operador que nosotros definimos pierda su efecto y en vez de esto se aplique el operador predeterminado para el trabajo con cadenas, en este caso la concatenación:
Caption := string(R1) + string(R2); //Esto devuelve "2/63/6"

¡Fantástico!, y ¿podríamos hacer combinaciones?? es decir,¿ aplicar el operador con secuencias de operandos de diferentes tipos??... Si, claro y también esto ofrece un gran poder, solo hay que ver esto:
Caption := R1 + R2 + '1/6'; //Esto devuelve "6/6" !!!

¡Estupendo!!...de la misma manera podriamos definir muchos mas operadores para el trabajo con números fraccionarios, por ejemplo:
  • Add +

  • Substract -

  • Multiply *

  • Divide /

  • Negative -

  • Equal =

  • NotEqual <>

  • LessThan <

  • LessThanOrEqual <=

  • GreaterThan >

  • GreaterThanOrEqual >=

Con lo que hemos visto hasta ahora es fácil imaginarse como sería la implementación del resto de los operadores, suerte y a practicar esta nueva funcionalidad.

No hay comentarios.:

Publicar un comentario