Mostrando entradas con la etiqueta enumerated. Mostrar todas las entradas
Mostrando entradas con la etiqueta enumerated. Mostrar todas las entradas

domingo, 29 de agosto de 2021

El bucle for in do.

La sentencia for in do (for in do loop) está disponible desde la versión 2.4.2 del compilador Free Pascal, es decir, es relativamente nueva y no existe ni en Pascal ni en Turbo Pascal, sí en Object Pascal de Delphi, aunque hay algunas "cosillas" que se pueden hacer con Free Pascal y no con Delphi, respecto de esta sentencia de iteración.

Seguramente para los nuevos (y no tan nuevos) programadores no hay ninguna novedad con este bucle, pero los que nos iniciamos mucho antes, dejamos la profesión unos años y retomamos en estos tiempos, hay cosas que cuestan un poco, no tenemos el concepto, nacimos con el for to do, while do, repeat until y de ser necesario, punteros, y con eso se hacía (y hace) de todo.

Las cosas cambian, los lenguajes evolucionan o mueren, aunque no siempre incorporar nuevas sentencias o lo que sea, signifique evolución, pero es preferible eso al estancamiento. Y la verdad que tanto Free Pascal y Delphi (ambos Object Pascal) conservan la belleza del lenguaje, esa sintaxis única.

Creo que lo mejor es empezar por responder ¿qué lo diferencia del for to do?:

En el for to se usa un índice, generalmente "i", entero, para recorrer los elemento de un vector (array) y acceder a ellos utilizando el valor de i, es decir obtenemos un valor de unarray[i]. En cambio en el for in para hacer lo mismo, usando una variable i y un array con índices enteros, la variable i va tomando los valores del array, los valores se obtienen de i. no de unarray[i].

Otra diferencia es que mediante el empleo de un for to se puede recorrer solo una parte, por ejemplo un array de 500 elementos con un for i:=1 to 10 do... solo accedemos a los 10 primeros, en cambio con el uso de for i in ... se recorre todo.

La variable de control debe ser del mismo tipo que los elementos del conjunto a recorrer.

El conjunto a iterar debe ser de un número fijo de elementos.

La variable es una copia temporal del elemento del bucle.

Ejemplos:

program pruebaforin;
{$mode objfpc}{$H+}
uses Classes;

type
  TSemana=(domingo, lunes, martes, miercoles, jueves, viernes, sabado);


var
  entero:Integer;
  arrEntero:array[1..12] of Integer=(1,2,4,8,5,6,7,8,9,27,11,12);
  cadena:String;
  arrCadena:array[1..5] of String =('Lunes','Martes','Miércoles','Jueves','Viernes');
  dia:TSemana;
  diaslaborables:set of TSemana =[lunes, martes, miercoles, jueves, viernes];
  caracter:Char;
  meses:TStringList;

begin
  WriteLn('Vector de eneteros');
  WriteLn('entero:Integer;');
  WriteLn('arrarrEntero:array[1..12] of Integer=(1,2,4,8,5,6,7,8,9,27,11,12);');
  WriteLn('for entero in arrEntero do WriteLn(''entero =  '',entero,''  arrEneter['',entero,'']='',arrEntero[entero]);');
  for entero in arrEntero do WriteLn('entero =  ',entero,'  arrEneter[',entero,']=',arrEntero[entero]);
  ReadLn;
  WriteLn('Vector de cadena de caracteres.');
  WriteLn('cadena:String;');
  WriteLn('arrCadena:array[1..5] of String =(''Lunes'',''Martes'',''Miércoles'',''Jueves'',''Viernes'');');
  WriteLn('for cadena in arrCadena do WriteLn(''Cadena = '',cadena);');
  for cadena in arrCadena do WriteLn('Cadena = ',cadena);
  ReadLn;
  WriteLn('Enumerados.');
  WriteLn('TSemana=(domingo, lunes, martes, miercoles, jueves, viernes, sabado); ');
  WriteLn('dia:TSemana;');
  WriteLn('for dia in Tsemana do WriteLn(''dia = '',dia);');
  for dia in Tsemana do WriteLn('dia = ',dia);
  ReadLn;
  WriteLn('Conjuntos.');
  WriteLn('TSemana=(domingo, lunes, martes, miercoles, jueves, viernes, sabado); ');
  WriteLn('diaslaborables:set of TSemana =[lunes, martes, miercoles, jueves, viernes];');
  WriteLn('dia:TSemana;');
  WriteLn('for dia in diaslaborables do WriteLn(''dia = '',dia);');
  for dia in diaslaborables do WriteLn('dia = ',dia);
  ReadLn;
  WriteLn('Caracteres.');
  WriteLn('caracter:Char;');
  WriteLn('for caracter in ''abcdefg'' do WriteLn(''caracter = '',caracter);');
  for caracter in 'abcdefg' do WriteLn('caracter = ',caracter);
  ReadLn;
  WriteLn('Classes.');
  WriteLn('cadena:String;');
  WriteLn('meses:TStringList;');
  WriteLn('for cadena in meses do WriteLn(''cadena = '',cadena);');
  meses:=TStringList.Create;
  meses.Add('Enero');
  meses.Add('Febrero');
  meses.Add('Marzo');
  meses.Add('Abril');
  meses.Add('Mayo');
  for cadena in meses do WriteLn('cadena = ',cadena);
  meses.Free;
  ReadLn;
end.      

Resultado del programa (salida por consola):

Vector de eneteros
entero:Integer;
arrarrEntero:array[1..12] of Integer=(1,2,4,8,5,6,7,8,9,27,11,12);
for entero in arrEntero do WriteLn('entero =  ',entero,'  arrEneter[',entero,']=',arrEntero[entero]);
entero =  1  arrEneter[1]=1
entero =  2  arrEneter[2]=2
entero =  4  arrEneter[4]=8
entero =  8  arrEneter[8]=8
entero =  5  arrEneter[5]=5
entero =  6  arrEneter[6]=6
entero =  7  arrEneter[7]=7
entero =  8  arrEneter[8]=8
entero =  9  arrEneter[9]=9
entero =  27  arrEneter[27]=0
entero =  11  arrEneter[11]=11
entero =  12  arrEneter[12]=12

Vector de cadena de caracteres.
cadena:String;
arrCadena:array[1..5] of String =('Lunes','Martes','Miércoles','Jueves','Viernes');
for cadena in arrCadena do WriteLn('Cadena = ',cadena);
Cadena = Lunes
Cadena = Martes
Cadena = Miércoles
Cadena = Jueves
Cadena = Viernes

Enumerados.
TSemana=(domingo, lunes, martes, miercoles, jueves, viernes, sabado);
dia:TSemana;
for dia in Tsemana do WriteLn('dia = ',dia);
dia = domingo
dia = lunes
dia = martes
dia = miercoles
dia = jueves
dia = viernes
dia = sabado

Conjuntos.
TSemana=(domingo, lunes, martes, miercoles, jueves, viernes, sabado);
diaslaborables:set of TSemana =[lunes, martes, miercoles, jueves, viernes];
dia:TSemana;
for dia in diaslaborables do WriteLn('dia = ',dia);
dia = lunes
dia = martes
dia = miercoles
dia = jueves
dia = viernes

Caracteres.
caracter:Char;
for caracter in 'abcdefg' do WriteLn('caracter = ',caracter);
caracter = a
caracter = b
caracter = c
caracter = d
caracter = e
caracter = f
caracter = g

Classes.
cadena:String;
meses:TStringList;
for cadena in meses do WriteLn('cadena = ',cadena);
cadena = Enero
cadena = Febrero
cadena = Marzo
cadena = Abril
cadena = Mayo

Para ejemplos más avanzados y documentación oficial:

Documentación: https://www.freepascal.org/docs-html/ref/refsu59.html

Wiki (Siempre y cuando algún iluminado no haya eliminado o movido la página): https://wiki.lazarus.freepascal.org/for-in_loop

Código fuente del ejemplo.

jueves, 4 de marzo de 2021

Cuadro de diálogo con casilla de verificación.

Desde la versión 1.8 de Lazarus, TTaskDialog está disponible en la pestaña o lengüeta de Dialogs. Se trata de un componente no visual.


En este caso veremos como incluir un par de botones comunes como si y no y un checkbox, porque no encontré en ningún lado como averiguar el estado del checkbox, pues lo habitual es que tenga una propiedad checked del tipo boolean, digo, si es un botón checkbox... pero no, es un enumerado y hay que buscarlo en Flags, si está ahí entonces sería True, caso contrario, False. Más rebuscado imposible, pero funcionar, funciona.
Este componente tiene varias opciones y se puede hacer bastante con él, en todos los ejemplos que vi (pocos, por cierto) se lo crea con código, lo que implica no usar el inspector de objetos; en este ejemplo, por lo tanto, se usa el inspector de objetos.

En un formulario poner 2 botones, 1 memo y un TTaskDialog. Un botón para comenzar y otro para cerrar o salir del programa.

En el inspector de objetos marcar las siguientes opciones e insertar los textos en las propiedades correspondientes.

Nota: no sé si es un bug o una nueva característica de Lazarus 2.X pero ahora para que el texto ingresado en una propiedad se refleje en el componte, una vez escrito el texto, hay que presionar Enter... obvio que es un pequeño y molesto bug que será reportado como corresponde para beneficio de todos.

 

Si dejamos la propiedad VerificationText vacía (o escrita pero sin presionar [Enter] entonces la casilla de verificación no se mostrará en el cuadro de diálogo. También se podría establecer mediante código: TaskDialog1.VerificationText:='No volver a pausar.".

Esta propiedad es un enumerado y al no estar vacía incorpora al conjunto de enumerados Flags el elemento tfVerificationFlagChecked. Por ende para saber si el usuario marco dicha casilla se debe preguntar si ese elemento está contenido en el conjunto Flags de esta forma:

tfVerificationFlagChecked in TaskDialog1.Flags

El código completo:

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    TaskDialog1: TTaskDialog;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  i:integer;
  SeguirPreguntando:Boolean=True; //Asociaremos con el checkbox
begin
  for i:=1 to 200 do
  begin
    if (i mod 10) = 0 then
      if SeguirPreguntando then
        if TaskDialog1.Execute then
          // Si presiona Sí o si está marcado el checkbox y se presionó Sí: la segunda condición
          // es necesaria porque si no, si se marca el checkbox y se presiona no produce resultados
          // no deasedos.
          if (TaskDialog1.ModalResult=mrYes) or ((tfVerificationFlagChecked in TaskDialog1.Flags) and (TaskDialog1.ModalResult=mrYes) )then
            SeguirPreguntando:=not (tfVerificationFlagChecked in TaskDialog1.Flags)
          else
            Break; // Se aborta el ciclo, por ende es imposible continuarlo, ya que no se interrumpe sino que
                   // directamente se aborta el for do.
    Memo1.Lines.Add(i.ToString);
    Application.ProcessMessages;
    Sleep(50);
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Close;
end;

end. 

Proyecto completo en GitLab

Documentación oficial de TTaskDialog

TTaskDialog en la Wiki


martes, 19 de enero de 2021

El selector Case.

El selector Case.

El poder de Case of en Pascal es muy grande, especialmente si se lo compara con cierto lenguaje. Por ejemplo permite rangos, valores separados por comas y una vez encontrada la coincidencia se sale del case, es decir, no se escribe un Break para finalizar cada sentencia.
Case trabaja con tipos de datos ordinales: enteros, enumerados, caracteres y cadenas. Los selectores deben ser todos del mismo tipo y literales (constantes) ya que se evalúan en tiempo de compilación.

Ejemplos:

const
  num1=10;
var
  num2:integer;
  i:integer;
...
i:=20;
Case i of
  num1 : writeLn('Es el número 10'); // Si i vale 10 se ejecuta esta sentencia y se sale del Case.
  num2 : writeLn('Es un entero'); // Error
  num1+10 : writeLn('Es el número 20');
else
   writeLn('Es otro número');
   writeLn('Pero no es el 10');
end;

Num2 es inválido porque no puede determinarse el valor de Num2 durante la compilación.
Num1 + 10 sí es válido ya que 10 + 10 = 20 es una expresión que se determina durante la compilación.
Else: también puede usarse Otherwise, es lo mismo, pero suele utilizarse Else. Nótese que no requiere begin .. end, no obstante se puede utilizar. Especificar Else no es obligatorio, si no encuentra el valor, simplemente se continúa con la siguiente sentencia del programa.

var
  c:Char;
...
case c of
  'a' : WriteLn('c es a');
  'b' : WriteLn('c es a');
  'c' : WriteLn('c es a');
  'a' : WriteLn('c es a'); // Error
end;

Error: no se pueden duplicar los selectores, aunque es algo más que obvio.

var
 s:String;
...
case s of
  'azul', 'rojo' : WriteLn('Son colores');
  'Debian', 'Linux Mint', 'Ubuntu' : WriteLn('Son sistemas operativos');
else
  WriteLn('Es otra cosa.');
end;

Este ejemplo no tiene errores.

var
  i,a:integer;
...
Case i of
  0 : begin
        WriteLn('El número es cero');
        a:=i+1;
      end;
  1..99 : WriteLn('Es un número de 2 dígitos');
  100, 101, 102..999 : WriteLn('Número de 3 dígitos');
else
  WriteLn('Mayor o igual a 1000');
end;

Desde ya, se pueden usar rangos y como vemos, en un mismo selector se pueden especificar varios valores. No es obligatorio que los valores estén ordenados, pero es una buena práctica al tratarse de enteros, pues facilita la lectura del código.

Otro ejemplo con Char:

var
  c:Char;
...
Case c of
  'A..Z', 'a..z' : WriteLn('Es una letra');
  '0..9' : WriteLn('Es un número');
  1..2 : WriteLn('E'); //Error
else
  WriteLn('No es ni letra ni número');
end;

Como vemos los rangos también se pueden utilizar con caracteres. El error se daría en 1..2 porque son enteros, no caracteres.

Para finalizar, un ejemplo con enumerados:

type
 TDia=(Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo);
var
  Dia:TDia;
...
Case Dia of
  Lunes..Viernes : WriteLn('Es un día laborable');
  Sabado         : WriteLn('A veces los sábados se trabaja');
  Domingo        : WriteLn('Es feriado o no laborable');
end;

El siguiente código lo utilizo en el programa Marcadores:

type
  TBuscar=(Duplicados,Errores,Error0,Error400,Error500,Redirect,OK200,Todos);
...
var
  QueBuscar:TBuscar;
...
function TFSeleccionar.CargarGrid: Boolean;
var
  i,f:Integer;
begin
  f:=0;
  for i:=Low(aReg) to High(aReg) do
  begin
    case QueBuscar of
      Todos : begin
                if ((aReg[i].chequear) and (not(aReg[i].Eliminar))) then
                 begin
                   Inc(f);
                   SGrid.InsertRowWithValues(f,['','','','']);
                   if aReg[i].borrar then SGrid.Cells[0,f]:='1' else SGrid.Cells[0,f]:='0';
                   SGrid.Cells[1,f]:=IntToStr(aReg[i].indice);
                   SGrid.Cells[2,f]:=aReg[i].URL;
                   SGrid.Cells[3,f]:=IntToStr(aReg[i].statuscode);
                   SGrid.Cells[4,f]:=IntToStr(aReg[i].Redirect);
                   SGrid.Cells[5,f]:=IntToStr(i);
                 end;
              end;
      Errores : begin
                  if ((aReg[i].chequear)  and (not(aReg[i].Eliminar)) and ((aReg[i].statuscode=0) or (aReg[i].statuscode>=400))) then
                  begin
                    Inc(f);
                    SGrid.InsertRowWithValues(f,['','','','']);
                    if aReg[i].borrar then SGrid.Cells[0,f]:='1' else SGrid.Cells[0,f]:='0';
                    SGrid.Cells[1,f]:=IntToStr(aReg[i].indice);
                    SGrid.Cells[2,f]:=aReg[i].URL;
                    SGrid.Cells[3,f]:=IntToStr(aReg[i].statuscode);
                    SGrid.Cells[4,f]:=IntToStr(aReg[i].Redirect);
                    SGrid.Cells[5,f]:=IntToStr(i);
                  end;
                end;
      Duplicados : begin
                     if ((aReg[i].chequear) and (not(aReg[i].Eliminar))) then
                       begin
                         Inc(f);
                         SGrid.InsertRowWithValues(f,['','','','']);
                         SGrid.Cells[0,f]:='0';
                         SGrid.Cells[1,f]:=IntToStr(aReg[i].indice);
                         SGrid.Cells[2,f]:=aReg[i].URL;
                         SGrid.Cells[3,f]:=IntToStr(aReg[i].statuscode);
                         SGrid.Cells[4,f]:=IntToStr(aReg[i].Redirect);
                         SGrid.Cells[5,f]:=IntToStr(i);
                       end;
                     end;
    end;
  end;
  Result:=SGrid.RowCount>0;
end;

miércoles, 6 de septiembre de 2017

Obtener el string de un enumerado

No existe algo así como EnumToStr pero desde luego hay formas sencillas de obtener o “convertir” (entre comillas) el valor en cadena de caracteres de un enumerado.

¿Cómo? Con el procedimiento Str.

Ejemplo:

Str ( ZQuery1.FieldDefs.Items[i].DataType, s );
showmessage(s)
;

A Str le pasamos el primer parámetro que es el enumerado y el segundo que es una variable del tipo string, ambos por referencia y el procedimiento asignará a la variable, en este caso “s” el valor string del enumerated.

procedure Str(var X: TNumericType[:NumPlaces[:Decimals]];var S: String)

Opcionalmente, se puede establecer el formato numérico, ya que Str también convierte tipos de datos numéricos, sean o no enumerados, aunque para los no enumerados hay funciones más completas y con la comodidad de ser funciones y no procedimientos.

Documentación de Str en Lazarus

Documentación de Str en Free Pascal