El futuro de Delphi parece más claro que nunca (Stefaan Lesage)

julio 1, 2009 en Artículos, ¿Sabías que...?, ¿Sabías que...? [Delphi], Consejo, Delphi, Entrada Diario, Noticias, Noticias Delphi

El futuro de Delphi parece más claro que nunca
Stefaan Lesage 23/06/2009
Traducción de su artículo en
http://www.devia.be/news/article/the-future-of-delphi-looks-brighter-than-ever-before/

Hace unas semanas, fui invitado a un encuentro cordial en Bruselas, con David Intersimone, más conocido como David I.  en la comunidad de Delphi. David nos iba a contar algo acerca del mapa de proyecto para Delphi.

Los ultimos 10 años, he estado desarrollando software usando Borland Delphi (más tarde en Codegear y ahora en Embarcadero) y esta era la oportunidad perfecta para sacar alguna información  de lo que aparece entre lineas.

Delphi para todo

El mensaje era muy claro, pero fue como una sorpresa para mi cuando David I. mencionó el objetivo principal: Delphi para todo.  Mas tarde, durante la sesión dijo: Delphi nativo para todo, mencionando MacOsX, Linux pero tambien dispositivos móviles. El incluso auguró la posibilidad de construir aplicaciones con Delphi para telefonía. (inclusive iPhone)

En ese momento, yo no supe que pensar acerca de eso, pero la posibilidad de desarrollar aplicaciones para dispositivos móviles sonaba bien … para ser honesto, sonaba como un sueño. Pero durante el curso del resto de presentaciones, yo advertí que ellos podrían hoy estar cercanos a cumplirlo.

image

Delphi Touch

Bien, ellos actualmente lo han llamado Delphi Natural Input para ser correcto. El fin es permitir a Delphi y a sus componentes de la VCL interactuar con diferentes formas de dispositivos de entrada, desde pantallas táctiles, tablas digitales, dispositivos sensores, gps, dispositivos de habla, Webcams,…

Todas esas características vendrán incluidas en una futura versión de la VCL de Delphi. Desde que trabajo sobre Mac, y estoy usando iPhone, tenía un buen conocimiento acerca de las posibilidades pero nunca pensé que fuera posible lograr algo así con Delphi. Bien, David I. nos enseñó una demo y me probó lo equivocado que estaba…

La demo era bastante sencilla, y no requería programación alguna. Todo lo que se tenía que hacer era añadir un nuevo componente GestureManager a la aplicación. El paso siguiente sería crear la “comunicación” o usar algunas de las ya predefinidas, disponibles en el GestureManager. La única cosa que se había hecho era añadir algo de código en el evento OnGestureEvent y ejecutar el código en el evento que lo activaría.

Para éstos que habían sido desarrollados en Delphi,  podrías actualmente compararlos al componente TActionList, en el cual se pueden añadir algunas acciones por defecto y permite que puedas añadir las propias también.

En este punto, empezaba a estar claro para mi que las posibilidades de crear aplicaciones para Dispositivos móviles usando Delphi ya no era un sueño. Aparentemente ellos ya habian ocupado bastante trabajo en ello y  se demostró que podía actualmente ser una realidad.

El mapa del proyecto Delphi (Delphi Roadmap)

Yo estaba contento de ver que el equipo de Delphi, estaba ocupado trabajando en la próxima salida de Delphi. Aparentemente los desarrolladores habían sido divididos en grupos de equipos trabajando unos pocos proyectos a reparar. Voy a dar una apunte de esos proyectos y sobre lo que ellos  enfocarán.  No me preguntes sobre fechas de salida, ya que ellos no mencionaron ninguna. Como David prudentemente dijo:  Hay 75 o más caminos de preguntarnos cuando estará preparado y solo hay una respuesta correcta: “Cuando esté preparado”.

Proyecto Weaver

El proyecto Weaver sera el principal foco de las grandes y pequeñas mejoras del IDE, que aumentarán la usabilidad y la productividad.
Con soporte añadido para dispositivos de entrada naturales como táctiles y de expresión,..
Mejorar la documentacion y aumentar la productividad de equipo.
DatanSnap con soporte http en el servidor REST, y .Net proxies para DataSnap.
IDE Insight ™ (teclado con acceso a todo)
Soporte del API de windows 7 y direct 2D
Soporte de  RTTI mejorado.
Control de código fuente con una nueva interface Open Tools Api que provee soporte para Gestor de Control de codigo fuentte ( Weaver tendrá alguna implementacion de Subversion)
.Net Aspect Oriented Programing -Programacion .Net orientada a Aspectos- (esto me pareció completamente impresionante)

Proyecto Delphi X

Plataforma cruzada Windows, Linux y MacOs.
Algunas otras caracteristicas a considerar:
Libreria de componentes compatible.
DataSnapX con la caracteristica de crear tanto el servidor como el cliente tanto en las tres plataformas y en la web.

Proyecto Chromium

La principal caracteristica de Chromium será la calidad, calidad y calidad.
La atención se pone sobre la productividad del desarrollador (mejoarar la usabilidad, eliminar las tareas largas detenidas, cerrar los bugs tan pronto como sea posible, fijando un nuevo estandar de reusabilidad.
Lightweig O/R mapping  -Mapeado ligero (Object/Relational)-
Documentacion  OTA (Open Tools Api)
Un nuevo modelo de mapeado de datos (DataBinding model)
Mayor integración con las herramienta de bases de datos.

Proyecto Commodore

Permitirá desarrollo de 64 bits nativo para delphi y c++ builder.
Algunas caracteristicas a considerar:
Compilador completo, con soporte RTL y VCL para compilación nativa de 64 bits.
Aplicaciones Multi-Core/Multi-Thread
Soporte para desarrollo paralelo en la RTL.

Y mucho mas por venir

Parece que hay mucho mas por venir y algunos proyectos podrían venir a la vez. Tristemente, no nos han dado información sobre cuando un producto estaría disponible al publico. La unica cosa que David I. mencionó que el plan era dar una nueva versión de delphi cada año.

TMS SMOOTH CONTROLS.

Aquí en Belgica contamos con pocos desarrolladores de Delphi y nosotros tambien tenemos compañias como TMSSoftware quienes estan desarrollando buenos componentes. Habían algunas personas de TMSSoftware en la sala y ellos nos dieron una pequeña demo del paquete TMS Smooth Control pack,  el cual es un conjunto rico en caracteristicas, de controles de sofisticada apariencia y animación.

Los controles relamente eran impresionantes e inmediatamente me dieron la impresión que se tendría cuando se usa una aplicación de iPhone. Los componente parence lilmpios, bonitos y con muchas animaciones.

Algunos de nosotros, que hemos estado usando iPhone conocemos lo que estoy comentando. Cuando navegamos desde una pagina a otra en la ventana de una aplicación iPhone tu tienes una buena animación en lugar de un simple repintado de la ventana. Bien, los Smooth Controls hicieron exactamente lo mismo sobre Windows. Esto nos dio la misma vista y presentación.

CONCLUSION

Me perdí la primera parte del dia debido al trafico pero llegue a tiempo para la parte del mapa de proyecto de Delphi y debo decir que me parecia impresionante. Han habido días cuando nosotros no sabiamos que esperar de Delphi y alguna gente pensó incluso que los viejos días se acabaron para Delphi. Para algunos de vosotros que todavíadudais acerca del futuro de Delphi… bien… dejarme que os diga:

El futuro de Delphi parece más claro que nunca!.

Nota de la traducción: Pido perdon si la traducción no es completmente literal y si pueden existir alguna que otra errata. Espero, de ser así recibir vuestros comentarios para subsanarlas.

Salvador Jover

Lo mas destacado… :-)

junio 14, 2009 en Advertencia, ¿Sabías que...?, ¿Sabías que...? [Delphi], Consejo, Delphi, Enlace interesante, Entrada Diario

Suelo reservar la tarde del domingo, un rato al menos, para hacer un repaso mental de lo que me ha parecido mas interesante a nivel de enlaces durante toda la semana. Quizás, antes -hablo de bastantes meses atrás- dedicaba mas tiempo a buscar paginas nuevas,  o entradas nuevas, si fuera el caso de que fueran conocidas y ya las hubiera incluido en mis enlaces, pero ahora, desde que empecé a incorporar los breves mensajes de Twitter (o Twibes) la situación cambió, y en lugar de mi busqueda tradicional, me he concentrado en seleccionar los comentarios que tuvieran mas interés.

El domingo anterior ya lo hice. Y si os dais cuenta, de no hacerlo así, muchos de esos comentarios pueden llegar a perderse ya que quedaran sepultados por los nuevos mensajes y por desgracia, algo como “voy a pasear al perro” en respuesta de “que es lo que estas haciendo” o “voy a trabajar duramente”, será inevitablemente el ejecutor de que no acaben siendo visibles y se oculten por los siglos de los siglos. Eso si algun dia NO deja de existir ese invento de las url cortas y nos dejan sin una referencia real a la que apuntar (ni forma de acceder) :-)

Huele que hay algo en esta nueva concepción de internet que no me acaba de gustar del todo. Recuerdo cuando en los foros tradicionales, donde recibiamos un correo en respuesta de la pregunta de un compañero, el moderador -yo en las ocasiones en las que lo he sido- era capaz de “recriminar” que un compañero X se fuera por los cerros de ubeda y nos contara su vida. Y eso era así porque los foros, en aquellos momentos, tenían un cariz mas “profesional” o al menos se entendía así. Era otro momento, y entiendase el recriminar, sin connotación negativa puesto que el principal objetivo de aquellos foros era basicamente apoyar al programador en sus tareas cotidianas.

Sin embargo, los años y la necesidad de marcar nuevas directrices y de llamar de otra forma a lo que ya existía, nos han hecho necesitar virtualmente conocer algo mas de los interlocutores y así, podemos llegar a saber que nuestro autor favorito un día se rompió el brazo o detalles mas escatológicos sobre su vida privada (dicho esto entre comillas). :-) Estamos casi obligados a tener un perfil repartido entre el facebock, twitter, los spaces de microsoft, blogger, wordpress, tuenti, etc… para existir en el mundo virtual.

Ya se que me repito. Pero son cosas que nos afectan. La servidumbre de asimilar estas nuevas ideas dentro de los blogs tienen costes adicionales. Yo por ejemplo, pagué ayer con una hora de sueño, cuando a las 2 de la mañana, acabando la entrada del blog y en el momento de hacerla publica, tras varias horas editandola, subiendo al servidor las imagenes, el codigo, revisando los errores… me di cuenta de que no se podía visualizarla. ¿Y por qué razon? Pues porque uno de los complementos o pluggins de wordpress generaba un error e impedía que se cargara correctamente la pagina. Así que me costó una hora descubrir que lo que realmente fallaba era el pluggin y localizarlo para intentar resolver el problema.

Hace poco, no fue una hora sino una tarde y por otro motivo similar…

Todo no es malo. No. Muchos de los mensajes contienen referencias a entradas que posiblemente nos pasarían inadvertidas, no porque no tuvieran importancia sino porque uno al final visita las paginas que conoce y dificilmente sale de su entorno.

Podriamos llamar a esta sección “And the winner is…”. Esta semana me quedo con la entrada http://alex.ciobanu.org/?p=232 de  Alexandru Ciobanu, en la que abordaba la posibilidad de extender la instancia de TObject y referenciar a una estructura o a otro objeto, que pueda tener un sentido dentro del dominio de nuestra aplicación.

Y me he decantado por esta (habia otra que hacia referencia al futuro de Delphi http://www.theregister.co.uk/2009/06/12/embarcadero_codegear_tools_future/) porque me ha permitido descubrir algunos detalles que desconocía.

* Que el tamaño en bytes de las instancias de TObject, fue modificado en Delphi 2009, pasando a 8 bytes en lugar de los 4 tradicionales. Sobre este punto encontre un artículo que introducía muy bien el tema y lo explicaba, y de paso, me permitía conocer la existencia de un puntero escondido a una nueva instancia de la clase TMonitor (que supuestamente era un tipo clase para sincronizar y facilitar los accesos concurrentes de los hilos de ejecución). O al menos entendí algo similar.

Así que una entrada me llevó a la otra: http://blogs.teamb.com/craigstuntz/2009/03/25/38138/ (Craig Stuntz) donde la información era mucho mas detallada sobre este punto, que habia levantado preguntas en algunos foros sobre el paso de 4 bytes a 8. (http://stackoverflow.com/questions/679022/what-data-does-a-tobject-contain)

Y tambien accidentalmente descubrí al hilo de todas estas investigaciones de sabueso, algunas entradas que comentaban aspectos de la VMT (la tabla de métodos virtuales)

Un del año 2000:  http://oreilly.com/catalog/delphi/chapter/ch02.html

y otra mas actual, que contiene unas imagenes muy directas para comprender que es lo que realmente está sucediendo al instaciarse TObject, entre bastidores.

http://pages.cs.wisc.edu/~rkennedy/vmt

Nada más.

Que tengais una buena semana.

:-)

Experimentos, reflexiones y otros artefactos (y V)

junio 14, 2009 en Artículos, Código, Consejo, Delphi, Entrada Diario, Sintaxis

He preferido que esta quinta entrega cerrara el título “Experimentos, reflexiones y otros artefactos”,  y despedir estos mini artículos o mini serie, si es que se pueden llamar así, con un ejemplo un tanto más avanzado, pero en la linea de los anteriores. Así que me habeis tenido aperreado toda la semana, dándole vueltas a la cabeza sobre cómo iba a finalizar estas entradas. :-)

No. No creais que es sencillo elegir el “experimento” ya que debe cumplir a priori algunas condiciones en cuanto a la extensión, a su complejidad, al uso de componentes que puedan ser compatibles en varias versiones y casi siempre en cuanto a que sea verdaderamente didáctico. Por lo que no vale cualquier idea que te venga a la cabeza, sino que tienes que pelearte con ella y ver si realmente te vale. Y os confieso cuando uno llega a casa tras la jornada diaria quedan pocas ganas de pelearse con nada (os pasará tambien a vosotros casi con seguridad).  :-)

En fin… Yo creo que sí que vale la pena el esfuerzo, y creo muchos de vosotros lo agradecereis.

¿Teneis ganas de trabajar un rato?

Venga, vamos allá.

Este era el ejemplo que se me ocurrió:  Imaginaba que pudieramos tener la necesidad de calcular el modo más óptimo de recorrer una cantidad de puntos indefinido, eso sí, recorriendolos todos, desde un origen cualquiera hasta un destino cualquiera. Esa era la primera idea que me me vino a la mente. La idea además era vistosa, porque podíamos dibujar los puntos y las lineas sobre el formulario, lo cual no era demasiado difícil. Además, nos permitía seguir abordando el uso de clases muy básicas como las listas de punteros (TList), que muchos compañeros que dan sus primeros pasos pudieran no estar demasiado familiarizados.

El problema es que tras esa idea, existe una complejidad inherente que puede ser tan grande como uno quiera, por lo que decidí intentar abordarlo de una forma sencilla, aun a pesar de que pueda no ser optima. Y el criterio que se sigue para ordenar el camino que nos conduce desde el nodo (rojo) hasta el nodo (azul) sigue una pauta que se rige en calcular la menor distancia con respecto al nodo anterior, una vez ordenado el vector.

De hecho, si os fijais he puesto lineas mas arriba “¿Teneis ganas de trabajar un rato?” y lo he hecho porque os dejo para vosotros si quereis adaptarlo para que no considere todos los nodos sino el camino mínimo (fijado previamente una numero de puntos por los que haya que pasar)  y no considere en la ruta todos, sino esa cantidad.

Al crearse el formulario, recrea una cantidad de nodos (he puesto  10 por poner una cifra) en puntos aleaotorios de un recuadro del formulario. El punto rojo es la salida (es otro nodo pero no es del vector). El punto azul el destino(tambien otro nodo que no existe en el vector). Ambos puntos, se enlazan a cada uno de los nodos del vector simplemente para que se pudiera ver una idea que voy a comentar posteriormente (*).

Esta es un imagen del formulario:

Asi que, como comentaba en lineas anteriores, vamos a recorrer el vector de nodos y reordenarlo sucesivamente hasta que se cumpla la precondición, que es que la ruta quede ordenada en función de la distacia al nodo que nos sirve de pivote, que va recorriendo la ruta que parte desde el punto de origen. Iniciamos en el punto de salida y buscamos el nodo mas cercano.  Luego el más cercano a este. Y asi sucesivamente hasta llegar al punto de destino. En la imagen que figura tras el código se puede apreciar bastante bien.

Vamos a ver el módulo que hemos escrito para expresar esta idea:

unit UNodo;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Math;

type
   TNodo = class;
   TLinea = class;

   TOrdenacion = procedure(Sender: TObject; ANodo:TNodo) of Object;

   TMalla = Class(TObject)
   private
    FPivote: TNodo;
    FLineas: TList;
    FNodos: TList;
    FZona: TForm;
    FSalida: TNodo;
    FDestino: TNodo;
    procedure SetSalida(const Value: TNodo);
    function GetLineas(Indice: Integer): TLinea;
    function GetNodos(Indice: Integer): TNodo;
    procedure InternalOrdenaNodos(Sender: TObject; ANodo: TNodo);
    procedure VaciarLineas;
    procedure VaciarNodos;
    procedure DibujaRuta;
    function CalculaMetricaRuta: Double;
   protected
    procedure DoOrdenaNodos(ANodo: TNodo; AOrdenacion: TOrdenacion); virtual;
    procedure DoDibujarMalla; virtual;
    procedure OrdenarNodos(ANodo: TNodo; ADestino: TNodo);overload; virtual;
   public
    FCambios: TStrings;
    Constructor Create(AForm: TForm); virtual;
    Destructor Destroy; override;
    procedure InicializarMalla;
    procedure OrdenarNodos; overload;
    Function CountLineas: Integer;
    Function CountNodos: Integer;
    procedure ActualizarMalla;
    function AddNodo: Integer;
    function AddLinea: Integer;
    function GenerarRuta: Double;
    property Salida : TNodo read FSalida;
    property Destino: TNodo read FDestino;
    property Lineas[Indice: Integer]: TLinea read GetLineas;
    property Nodos[Indice: Integer]: TNodo read GetNodos;

   End;

   TLinea = Class(TObject)
   private
    FMalla: TMalla;
    FAlfa: TNodo;
    FOmega: TNodo;
    FDistancia: Double;
    FIndice: Integer;
    procedure SetAlfa(const Value: TNodo);
    procedure SetOmega(const Value: TNodo);
    procedure SetIndice(const Value: Integer);
   protected
   public
    function GetDistancia: Double;
    Constructor Create(AMalla: TMalla); virtual;
    Destructor Destroy; override;
    procedure Enlaza(AAlfa,AOmega: TNodo); overload;
    procedure DibujarLinea;
    property Alfa: TNodo read FAlfa write SetAlfa;
    property Omega: TNodo read FOmega write SetOmega;
    property Indice: Integer  read FIndice write SetIndice;
   End;

  TNodo = Class(TObject)
  private
    FMalla: TMalla;
    FShape: TShape;
    FPoint: TPoint;
    FPeso: Integer;
    FTag: Integer;
    function GetX: Integer;
    procedure SetX(const Value: Integer);
    function GetY: Integer;
    procedure SetY(const Value: Integer);
    procedure SetPeso(const Value: Integer);
    procedure SetTag(const Value: Integer);
   protected
   public
    function GetDistancia: Double;
    function GetIndice: Integer;
    function AddLinea(ANodoTarget: TNodo): Integer;
    Constructor Create(AMalla: TMalla); virtual;
    Destructor Destroy; override;
    procedure DibujarNodo;
    property X: Integer read GetX write SetX;
    property Y: Integer read GetY write SetY;
    property Peso: Integer read FPeso write SetPeso;
    property Distancia: Double read GetDistancia;
    property Tag: Integer read FTag write SetTag;
  End;

implementation

function CalcularDistanciaNodos(AAlfa, AOmega: TNodo): Double;

        //Necesitamos dos puntos (x1,y1) (x2,y2)
        // [x1,y1] [x2,y2]
        // Formula Raiz de ( ((x2-x1) al cuadrado) + ((y2-y1) al cuadrado) )

      function CalcularDistancia(X1, Y1, X2, Y2: Integer): Double;
      begin
         Result:= SQrt(Power((X2-X1), 2) + Power((Y2-Y1), 2));
      end;
begin
  if (AAlfa = Nil) or (AOmega = Nil) then Result:= 0
  else Result:= CalcularDistancia(AAlfa.X, AAlfa.Y, AOmega.X, AOmega.Y);
end;

{ TMalla }

procedure TMalla.ActualizarMalla;
begin
  DoDibujarMalla;
end;

function TMalla.AddLinea: Integer;
begin
   Result:= FLineas.Add(TLinea.Create(Self));
   TLinea(FLineas[Result]).Indice:= Result;
end;

function TMalla.AddNodo: Integer;
begin
   Result:= FNodos.Add(TNodo.Create(Self));
end;

constructor TMalla.Create(AForm: TForm);
begin
  if AForm = Nil then
    Raise Exception.Create('Atención: La referencia no es valida');
  FZona:= AForm;

  FSalida:= TNodo.Create(Self);
  FDestino:= TNodo.Create(Self);

  FNodos:= TList.Create;
  FLineas:= TList.Create;
end;

destructor TMalla.Destroy;
begin
  //limpiamos la lista de nodos
  VaciarNodos;
  FreeAndNil(FNodos);
  //limpiamos la lista de lineas
  VaciarLineas;
  FreeAndNil(FLineas);
  //nos desvinculamos del objeto padre
  //sobre el que nos mostramos
  FZona:= Nil;

  FreeAndNil(FSalida);
  FreeAndNil(FDestino);

  inherited Destroy;
end;

procedure TMalla.DibujaRuta;
var
  i: Integer;
  FNodo: TNodo;
begin
  VaciarLineas;

  fNodo:= Nil;
  for i := 0 to CountNodos - 1 do begin
    if FNodo <> Nil then begin
       FNodo.AddLinea(Nodos[i]);
    end
    else FSalida.AddLinea(Nodos[i]);
    FNodo:= Nodos[i];
  end;
  FNodo.AddLinea(FDestino);
end;

procedure TMalla.DoDibujarMalla;
var
  i: Integer;
begin
  if Assigned(FZona) then begin
     for i := 0 to CountLineas - 1 do Lineas[i].DibujarLinea;
     for i := 0 to FNodos.Count - 1 do Nodos[i].DibujarNodo;
     Salida.DibujarNodo;
     Destino.DibujarNodo;
  end;
end;

procedure TMalla.DoOrdenaNodos(ANodo: TNodo; AOrdenacion: TOrdenacion);
begin
  AOrdenacion(Self, ANodo);
end;

function TMalla.GenerarRuta: Double;
begin
  if (CountNodos = 0) or (FSalida = Nil) or (FDestino = Nil) then
    Raise Exception.Create('Error: No existe suficente información en la ruta a generar');
  OrdenarNodos;
  DibujaRuta;
  Result:= CalculaMetricaRuta;
end;

function TMalla.GetLineas(Indice: Integer): TLinea;
begin
   if (Indice < 0) or (Indice > FLineas.Count-1) then
      Raise Exception.Create('Error en el valor del indice de la matriz');
  Result:= FLineas[Indice];
end;

function TMalla.GetNodos(Indice: Integer): TNodo;
begin
   if (Indice < 0) or (Indice > FNodos.Count-1) then
      Raise Exception.Create('Error en el valor del indice de la matriz');
   Result:= FNodos[Indice];
end;

procedure TMalla.InicializarMalla;
begin
   VaciarLineas;
   VaciarNodos;
   FPivote:= Nil;
end;

procedure TMalla.InternalOrdenaNodos(Sender: TObject; ANodo: TNodo);

   procedure OrdenaQuickSort(L,R: Integer);
   var
     i,j: Integer;
     piv, aux: TNodo;
   begin
     i:= L;
     j:= R;
     piv:= Nodos[(L+R) div 2]; //pivote
     Repeat
       while (Nodos[i].GetDistancia < piv.GetDistancia) do Inc(i);
       while (piv.GetDistancia < Nodos[j].GetDistancia) do Dec(j);
       if i<= j then begin
          Aux:= Nodos[i];
          FNodos.Exchange(i,j);
          FNodos[j]:= Aux;
          Inc(i);
          Dec(j);
       end;
       if L<j then OrdenaQuickSort(L,j);
       if i<R then OrdenaQuickSort(i,R);
     Until i>j;
   end;  

begin
   OrdenaQuickSort(ANodo.GetIndice, CountNodos-1);
end;

procedure TMalla.OrdenarNodos;
var
  i: Integer;
begin
  FPivote:= FSalida;
  for i := 0 to CountNodos - 1 do
     OrdenarNodos(Nodos[i], FDestino);
end;

procedure TMalla.OrdenarNodos(ANodo: TNodo; ADestino: TNodo);
var
  i: Integer;
begin
  //guardamos el indice del vector para que calcule la distancia
  //correctamente ya que el pivote debe avancar
  i:= ANodo.GetIndice;
  DoOrdenaNodos(ANodo, InternalOrdenaNodos);
  //en este punto el nodo puede haber cambiado
  FPivote:= Nodos[i];
end;

procedure TMalla.SetSalida(const Value: TNodo);
begin
  if FSalida = nil then
     FSalida := Value;
end;

procedure TMalla.VaciarLineas;
var
  i: Integer;
begin
  for i := 0 to CountLineas - 1 do begin
    TLinea(FLineas[i]).Free;
    FLineas[i]:= Nil;
  end;
  FLineas.Clear;
end;

procedure TMalla.VaciarNodos;
var
  i: Integer;
begin
  for i := 0 to CountNodos - 1 do begin
    TNodo(FNodos[i]).Free;
    FNodos[i]:= Nil;
  end;
  FNodos.Clear;
end;

function TMalla.CalculaMetricaRuta: Double;
var
  i: Integer;
begin
  Result:= 0;
  for i := 0 to CountLineas - 1 do begin
    Result:= Result + TLinea(FLineas[i]).GetDistancia;
  end;
end;

function TMalla.CountLineas: Integer;
begin
   Result:= FLineas.Count;
end;

function TMalla.CountNodos: Integer;
begin
  Result:= FNodos.Count;
end;

{TNodo}

procedure TNodo.DibujarNodo;
begin
  if not Assigned(Self) then Exit;

  if (Self = FMalla.Salida) or
     (Self = FMalla.FDestino) then begin
    if (Self = FMalla.Salida) then begin
      FShape.Brush.Color:= clRed;
      FShape.Pen.Color:= clRed;
    end
    else begin
      FShape.Brush.Color:= clBlue;
      FShape.Pen.Color:= clBlue;
    end;
  end
  else begin
    FShape.Brush.Color:= clLime;
    FShape.Pen.Color:= clGreen;
    FMalla.FZona.Canvas.Font.Size:= 7;
    FMalla.FZona.Canvas.TextOut(X-12,Y-12,'('+InTToStr(GetIndice)+')')
  end;
end;

function TNodo.AddLinea(ANodoTarget: TNodo): Integer;
var
  fLin: TLinea;
begin
   if ANodoTarget = Nil then
     Raise Exception.Create('Atención: La referencia al nodo no es valida');
   Result:= FMalla.AddLinea;
   if Result >= 0 then
      FMalla.Lineas[Result].Enlaza(Self, ANodoTarget);
end;

constructor TNodo.Create(AMalla: TMalla);
begin
  if AMalla = Nil then
     Raise Exception.Create('Atención: La referencia no es valida');
  FMalla:= AMalla;

  FShape:= TShape.Create(Nil);
  FShape.Parent:= AMalla.FZona;
  FShape.Shape:= stCircle;
  FShape.Height:= 8;
  FShape.Width:= 8;
  if Self = FMalla.Salida then begin
    FShape.Brush.Color:= clRed;
    FShape.Pen.Color:= clRed;
  end
  else begin
    FShape.Brush.Color:= clLime;
    FShape.Pen.Color:= clGreen;
  end;

  FPeso:= 0;

  x:= 0;
  y:= 0;

end;

destructor TNodo.Destroy;
begin
  FMalla:= Nil;
  FreeAndNil(FShape);
  inherited;
end;

function TNodo.GetDistancia: Double;
begin
  Result:= CalcularDistanciaNodos(FMalla.FPivote, Self);
end;

function TNodo.GetIndice: Integer;
var
  i: Integer;
begin
   i:= 0;
   while i < FMalla.CountNodos do begin
     if FMalla.Nodos[i] = Self then begin
       Result:= i;
       Exit;
     end;
     Inc(i);
   end;
   Result:= -1;
end;

function TNodo.GetX: Integer;
begin
  Result:= FPoint.X;
end;

procedure TNodo.SetTag(const Value: Integer);
begin
  FTag := Value;
end;

procedure TNodo.SetPeso(const Value: Integer);
begin
  FPeso := Value;
end;

procedure TNodo.SetX(const Value: Integer);
begin
  FPoint.X := Value;
  FShape.Left:= FPoint.X;
end;

function TNodo.GetY: Integer;
begin
  Result:= FPoint.Y;
end;

procedure TNodo.SetY(const Value: Integer);
begin
  FPoint.Y := Value;
  FShape.Top:= FPoint.Y;
end;

{ TLinea }

constructor TLinea.Create(AMalla: TMalla);
begin
  FMalla:= AMalla;
  FAlfa:= Nil;
  FOmega:= Nil;
end;

destructor TLinea.Destroy;
begin
  FMalla:= Nil;
  inherited;
end;

procedure TLinea.DibujarLinea;
begin
   if Assigned(FMalla) then begin
      FMalla.FZona.Canvas.MoveTo(FAlfa.X, FAlfa.Y);
      FMalla.FZona.Canvas.LineTo(FOmega.X, FOmega.Y);
   end;
end;

function TLinea.GetDistancia: Double;
begin
  Result:= FDistancia;
end;

procedure TLinea.Enlaza(AAlfa, AOmega: TNodo);
begin
   if AAlfa = AOmega then
      Raise Exception.Create('No se pueden enlazar dos nodos iguales');

   if AAlfa <> FAlfa then FAlfa:= AAlfa;
   if AOmega <> FOmega then FOmega:= AOmega;

   FDistancia:= CalcularDistanciaNodos(FAlfa, FOmega);
end;

procedure TLinea.SetAlfa(const Value: TNodo);
begin
  if Value <> FOmega then FAlfa := Value;
  FDistancia:= CalcularDistanciaNodos(FAlfa, FOmega);
end;

procedure TLinea.SetIndice(const Value: Integer);
begin
  FIndice := Value;
end;

procedure TLinea.SetOmega(const Value: TNodo);
begin
  if Value <> FAlfa then FOmega := Value;
  FDistancia:= CalcularDistanciaNodos(FAlfa, FOmega);
end;

end.

Y esta es la imagen, una vez generada la ruta:

Descargar

El asterisco (*) venía a cuento cuando comentaba que podíais modificar facilmente el ejemplo para que calculara la ruta no en función de recorrer todos los nodos sino aquellos que pudieran hacer menor la distancia. Para ello, quizás bastaría modificar el calculo de la distancia, sobrescribiendo la función virtual

DoOrdenaNodos(ANodo: TNodo; AOrdenacion: TOrdenacion); virtual;

Y entregando como parámetro una nueva función que ademas, tuviera en cuenta no solo la distancia de los dos nodos sino también el calculo con respecto al punto de destino. Y una vez obtenido ese recalculo, compararlo en cada uno de los avances del vecto con el punto final. Podría ser una idea.

Como hicimos en la entrada anterior vamos a intentar destacar algunas ideas importantes que os pueden servir de guia:

* Este es un ejemplo y no deja de serlo aunque calcule un resultado. Os comento ésto porque este resultado puede ser correcto para mi, en el dominio de una aplicación concreta y no ser para otros requerimientos, puesto que no existe realmente una optimización al generar la ruta, ni existen caminos que precondicionen optar por una ruta o por  otra. Aqui hemos cogido puntos al azar y nos hemos imaginado la forma de recorrerlos todos.

* Otro punto que parece interesante y por eso lo he incluido así, es el paso de una función como parámetro, que nos permite reutilizar el metodo ante la necesidad de tener en cuenta nuevas condiciones. Para ello, nos basta sobrescribir el método, que es algo realmente sencillo. En este caso concreto, el método tiene un tipo declarado en el interfaz de la unidad UNodos.pas (TOrdenacion = procedure(Sender: TObject; ANodo:TNodo) of Object;)

* Y puede seros de curiosidad, por ejemplo el algoritmo QuickSort, que necesité modificar un tanto para que se reordenara en función de la distancia, como he explicado en lineas anteriores.

* El tratamiento de las listas de punteros (TList) y de las listas de cadenas y como podemos valernos de estas clases para almacenar y manipular referencias a objetos. Es un punto que se reitera continuamente en buena parte del código que podais encontrar, ya que son clases muy básicas.

* Y como siempre, teniendo en cuenta la reutilización. Aunque no lo he comentado, creo que incluso podríamos haber hecho uso del generador de códigos (visto en la entrada anterior), haciendo una ligera modificiación para que nos pudiera calcular distintas combinaciones de nodos y ofrecer a nuestro usuario, la posibilidad de elegir entre varias rutas alternativas.

Con un poco de imaginación quizás no hablariamos de distancia. Es una de las razones que me han hecho añadir la propiedad Peso en el Nodo, aunque luego no le he dado ninguna utilidad. ¿Una combinación de factores, distancia y peso y otros que pudieramos considerar? ¡Hay tantas posibilidades que en nuestro pequeño ejemplo nos hemos intentado quedar con la más didáctica y no enrevesar el código con mayor complejidad que no nos iba a aporatar realmente nada.

Yo creo que podemos dejarlo en este punto. La idea era compartir con vosotros las ventajas de razonar en términos de clases, de intentar en la medida que nos sea posible abstraernos y hacer que nuestro código sea lo mas reutilizable posible. Si bien es cierto que a corto plazo puede ser un inconveniente porque implica un mayor esfuerzo, a largo plazo es rentable, tanto a nivel de reusabilidad como de adaptabilidad a nuevos requerimientos.

Espero que os puedan ayudar estas lineas.


Nuevo Twibe delphiespanol: ¿Te unes?

mayo 17, 2009 en ¿Sabías que...?, Consejo, Delphi, Enlace interesante, Entrada Diario, Noticias, Noticias Delphi

Dichosa ñ, como se le tira de menos.  :-)

Hoy podemos compartir una noticia bastante positiva. Si existía una tribu para los programadores de delphi de habla inglesa, ¿por qué no también para los programadores hispanos? Así que cuando he visto el mensaje de Andreano Lanusse donde comentaba que ya se habia abierto esta comunidad en twibes, he pensado que no habia tiempo que perder para apuntarse, y de la misma forma que me habia suscrito en http://www.twibes.com/group/delphi, tambien lo he hecho en http://www.twibes.com/group/delphiespanol, con mas razón todavía.

Twibes Hispano

De hecho, comparto con vosotros que estoy dando mis primeros pasos para mejorar mi inglés, que al día de hoy me permite leer la documentación que necesito, pero desgraciadamente no me permite poder seguir los videos o escribir con corrección y participar de recursos que pueda llevar a mi comunidad. Esa es un poco la idea y en ello estoy. Tiempo al tiempo.  

Por otro lado, tengo la esperanza de que todas estas iniciativas que están llevando a cabo potenciarán nuestra comunidad y nuestra herramientas de desarrollo. Nos dan una oportunidad para salir de los silencios y llevar nuestra voz y nuestras inquietudes mas lejos. ¿De verdad vais a dejarlas pasar?

Experimentos, reflexiones y otros artefactos (I)

mayo 13, 2009 en Advertencia, Artículos, Código, Consejo, Delphi, Entrada Diario, Taller práctico

Vamos a iniciar una pequeña serie especialmente dedicada a los programadores que se inician en Delphi y me perdonareis que no tenga la menor idea de cuantos capítulos contiene ni de cuanto tiempo se pueda extender. ¿Uno, dos? ¿quizás tres? ¿veintiuno?. Realmente os confieso que no lo se. Habitualmente se cierran las series en funcion de varias premisas: que hayas dicho todo (resulta raro), que no tengas tiempo para continuarla (supone cerrar uno o varios capitulos rápidamente e intentando que la persona que te lee no note que tienes prisa por acabar) y finalmente, que hayas iniciado un tema y no tengas ni pajolera idea de como acabarlo, bien porque has llegado a un callejon sin salida, bien porque ya no sabes como sacarle mas miga al asunto .

En realidad, mientras escribo me vienen muchas ideas a la cabeza, fruto casi siempre del día a día, pero creerme que resultan dificiles de abstraer a un contexto que sea ejemplar y reducido, como lo puede ser el contenido de un blog. La idea principal, para que me vayais comprendiendo, es quizás concienciar a este programador que se inicia en el entorno, que sufrirá durante toda su vida la tentación de olvidarse de las clases y de la orientación a objetos, para convertirse en un mero “usador” indiscriminado de ellos. Es una paradoja.  Y también una de las maldades que vienen implícitas en el sistema, como el lado oscuro de la fuerza que acompañaba al mítico Jedi. Anteriormente ya he comentado cosas como esta, que siempre he recalcado, y es precisamente por esa razón por lo que el destinatario de estas reflexiones o experimentos, es el programador que da sus primeros pasos en el entorno… 

 ¡venga!, ¡vamos!. Si. Tú… Ehhhh… Que te digo a ti…¿no tenías otras cosas que hacer…? ¡No te hagas el remolón que Tú ya sabes mas que las ratas coloradas!. ;-)   Y esta serie es solo para los programadores que se inician…

Ahora que nos hemos quedado solos (vosotros y este mendrugo que os escribe) y que nos dejan trabajar con tranquilidad, podemos hablar de las cosas sencillas.

Vamos a empezar por un ejemplo muy básico que nos permitirá ir avanzando en estas reflexiones.

Imaginaros que tenemos una producto cualquiera en nuestra manos. Podría ser una bicicleta o un despertador. ¡Qué mas da!. Lo que querais imaginar nos puede servir.

Nos vamos a valer de ese imaginario producto para hacer un ejercicio de abstracción que nos permita asignarle un tipo y un color. Y si llegamos un poco mas lejos, incluso podemos concretar que ambos tipos se definen mediante una cadena de dos caracteres que pueden ser enlazados para formar un hipotético código.

Puestos en esa tesitura, podriamos escribir unas lineas como estas:

//al pulsar el boton Solucion
procedure TmainRelaciones.btnSolucion1Click(Sender: TObject);
var
 i,j: Integer;
begin
   for i := 0 to lbxTipo.Count - 1 do
      for j := 0 to lbxColor.Count - 1 do
          lbxResultados.Items.Add(lbxTipo.Items[i]+ lbxColor.Items[j]);
end;

//al crearse el formulario
procedure TmainRelaciones.FormCreate(Sender: TObject);
begin
   lbxTipo.Items.Add('AA');
   lbxTipo.Items.Add('AB');
   lbxTipo.Items.Add('CC');

   lbxColor.Items.Add('10');
   lbxColor.Items.Add('12');
   lbxColor.Items.Add('15');
end;

lbxTipo, lbxColor y lbxResultados, son tres objetos de la clase TListBox que hemos dejado caer en nuestro formulario. El último, lbxResultados, nos permite concatenar una cadena de 4 caracteres y…

Sencillo. Vale. Hemos resuelto el problema y nuestro jefe estará orgulloso de nosotros porque somos la repera y le hacemos ganar dinero facilmente, PERO no hemos pensado en terminos de objetos ni de clases… Dos bucles for, anidados nos permitieron crear la estructura de un hipotetico código compuesto. Nos ha bastado encontrar un clase (contenedor) para resolver la permanencia y ¡voila!… ¿Os habeis dado cuenta como he remarcado la palabra PERO en la oración anterior?

Es una verdadera pena porque pensar en terminos de clases es bastante divertido y hasta saludable. Y no es que al razonar en  terminos de clases no pueda usarse un bucle for… ¡faltaría mas! ¡alguno habrá que todavía me lo recrimine en los post posteriores!

Como primera reflexión, esta bien ¿no?… 

Parece que no estáis demasiado conformes!. :-)  

Bueno. Vale. Extenderé el experimento y nuestro jefe, con ese aire transcendental que le caracteriza, nos comentará que en ocasiones es importante que se puedan alterar el orden de los atributos del artefacto.

Ummmmmmmmmmmmmmmmmmm….

-¡Vale! ¡Ya está!

Nuestro programador siempre encuentra soluciones y en este caso lo resolvió haciendo uso de otro componente, concretamente de la clase TRadioGroup. Éste y una estructura case, y su jefe vuelve a sonreir… :-)

procedure TmainRelaciones.btnSolucion1Click(Sender: TObject);
var
 i,j: Integer;
begin
   for i := 0 to lbxTipo.Count - 1 do
      for j := 0 to lbxColor.Count - 1 do
          case rgpCodigo.ItemIndex of
           0: lbxResultados.Items.Add(lbxTipo.Items[i]+ lbxColor.Items[j]);
           1: lbxResultados.Items.Add(lbxColor.Items[j]+ lbxTipo.Items[i]);
          end;
end;

 

Para este programador el formulario es una gran cajon donde se deposita todo y lo mas peligroso de estos razonamientos no es cómo son sus comienzos sino cuales son sus finales.

En sucesivas entradas intentaremos complicarle la vida a nuestro programador y si os parece, de forma similar, le propondremos algunos razonamientos en función de la programación orientada a objetos.

Por curiosidad (y Parte III)

abril 8, 2009 en Ado Express & DataSnap, Advertencia, Artículos, ¿Sabías que...? [Delphi], Código, Consejo, Delphi, Entrada Diario

Nos habiamos quedado en la propiedad Delta…

Tanto la propiedad Data como Delta se definen como OleVariants, y se organizan internamente como un array de bytes, y es esta característica la que va a dotar de flexibilidad a las dos estructuras, que soportaran por un lado los datos, el contenido real, en el caso de la propiedad Data, y un registro de actualizaciones que representa a la propiedad Delta. Dicho registro, logicamente es de solo lectura, dado que es la unica forma de garantizar que es tan solo manipulable por el propio dataset. Tambien es por esa razón, ya que no nos es permitido modificarlo, es por la que existen metodos que nos permiten limpiar esa cache de datos que contiene los registros de cambios.

Pero lo mejor es que lo veamos con unos cambios. Lo primero que he hecho es modificar el navegador de articulos para que nos deje insertar, habiltando todas las opciones. Como solo vamos a poner nuestra atención en la tabla de articulos nos podemos olvidar de los detalles y del mecanismo para transmitir el valor a las claves ajenas desde la inserción del articulo. Sobre este punto, yo os volvería a remitir a los cursos de Ian Marteens, accesibles desde su web a un precio razonable. Esto con independencia de que podamos mas adelante comentar este punto. El framework que desarrolla Martens en cada capitulo de esos cursos aborda una buena cantidad de detalles necesarios para trabajar con el esquema DataSnap, tanto a dos como a mas capas.

Os propongo unos cambios en los datos de la tabla articulos. Los hago y vemos los resultados:

* Añadimos un registro a la tabla articulos.
  Articulo Descripcion
(Valor nuevo) A0006 Articulo 0006
* Modificamos el articulo con codigo A0005.
  Articulo Descripcion
(Valor anterior) A0005 Articulo 0005
(Valor nuevo) Art5 Otra descripcion
* Eliminamos tambien el artículo con codigo A0002.
  Articulo Descripcion
(Valor anterior) A0002 Articulo 0002

 
Ver delta

La rejilla inferior os muestra el contenido del registro de actualizaciones. La estructura es identica a nivel de campos, a la que contiene la propiedad Data, como ya hemos comentado. Pero en el delta, por cada registro existente en el data, pueden existir máximo un par de registros vinculados a éste. Segun el caso, ya que para las inserciones o los borrados tan solo necesitamos un registro y mantener en algun “sitio” algo que nos diga si es un borrado o una inserción (en ese punto interviene UpdateStatus con los valores [usModified, usInserted, usDeleted]). En el caso de las modificiones, el primero registro representa el contenido original y el segundo los nuevos valores que toma.

Si yo modificara repetidamente el registro correspondiente al articulo A0005, los cambios sobrescribirian repetidamente el segundo integrante del par, lo cual no es ni bueno ni malo, pero si es esclarecedor, si en algun momento piensas, al revertir los cambios, un comportamiento similar al de cualquier deshacer (ej Word en office) de las aplicaciones mas habituales.

Y retomando ya el motivo que originó estas tres entradas, ahora que ya estamos en contexto de poder razonarlo, parece dificil encontrar la forma de generar un metodo que de forma limpia y sencilla provoque el refresco de uno de los detalles cualquiera sin tener que liarse a crear nuevos conjuntos de datos auxiliares que invoquen en un segundo plano el contenido a importar. Quiero decir que no parece sencillo añadir un nuevo metodo o una nueva función que nos permita decirle:
- ¡Oye, majo!, que no quiero los detalles de la tabla composicion. Haga Vd el favor de mostrarme los valores correctos…

Y el problema en mi opinión (y como siempre digo es mi opinión y no tiene porque coincidir ni ser acertada) es el diseño en bloque de ambas cachés. Si tuviera que buscar una analogia quizás lo compararia a esas muñecas rusas que nunca sabes por el exterior cuantas van a contener salvo que las vayas abriendo de una en una hasta llegar hasta la que tu deseas.

Muñecas rusas

Nunca llueve a gusto de todos, decía mi padre. Así que me queda la pregunta si hubiera podido existir una alternativa que hubiera contemplado que estas cachés se hubieran repartido de forma equitativa a cada uno de los componentes ClientDataSet, dejando que cada palo hubiera aguantado su vela.

Ni idea… :-(