Boletín Pascal #6
INDICE
1. UNAS PALABRAS DEL EDITOR
2. HERRAMIENTAS GRATUITAS DE BORLAND
3. ESCANEANDO ARCHIVOS COMPRIMIDOS
4. EL NOVATO Y EL EXPERTO
Constantes, variables y expresiones lógicas (booleanas)
________________________________________________________________________
1. UNAS PALABRAS DEL EDITOR
Muy pronto les estaremos enviando a todos nuestros suscriptores un
cuestionario por email. Este cuestionario nos ayudará a evaluar este
newsletter y ver que podemos hacer para satisfacer mejor sus necesi-
dades. Es muy importante que contesten las preguntas y lo devuelvan
para hacer que su opinión cuente en la definición de como serán las
futuras ediciones. Contestar va a ser bastante rápido ya que la mayoría
de las preguntas tendrán un conjunto de respuestas predefinidas para que
ustedes elijan. Apreciaríamos mucho si se pudieran tomar el tiempo en
participar de esta encuesta. Desde ya muchas gracias.
Saludos,
Ernesto De Spirito
eds2008 @ latiumsoftware.com
________________________________________________________________________
JfControls Lib. Multilenguaje. Multiapariencia. Skins. Privilegios. Más
de 40 componentes integrados y personalizables. Múltiples problemas de
programación resueltos. Administración centralizada de recursos. Para
Delphi 3-2006 y C++ Builder 3-6. http://www.jfactivesoft.com/spindex.htm
________________________________________________________________________
2. HERRAMIENTAS GRATUITAS DE BORLAND
Unos pocos meses atrás, Borland lanzó el compilador Borland C/C++ 5.5
como freeware, una versión en línea de órdenes de 32 bits del compilador
usado por C++ Builder, y viene con otras utilidades en línea de órdenes
(preprocesador, enlazador, compilador y enlazador de recursos y otras
cosas), ayuda (restringida a la operación de las herramientas única-
mente), ficheros "include" y bibliotecas, y algunos ejemplos, pero por
supuesto no incluye ni la VCL ni una referencia de C++. Si está inte-
resado en descargarlo, puede encontrarlo aquí:
http://www.borland.com/bcppbuilder/freecompiler/ (7.79 Mb)
Más recientemente Borland también lanzó el Turbo Debugger como freeware,
y no necesito decirles cuánto necesitarán esta herramienta para
descubrir qué es lo que está mal en sus programas!
http://www.borland.com/bcppbuilder/turbodebugger/ (590 Kb)
No llegarán a la luna con estas herramientas, pero pueden pedir prestado
algún viejo libro de C++ y aprender un poquito de C++ si quieren. ;)
________________________________________________________________________
3. ESCANEANDO ARCHIVOS COMPRIMIDOS
En el último número presentamos el componente TZipMaster y las DLLs Zip
y Unzip (ZIPDLL.DLL y UNZDLL.DLL). Tal como prometimos, en este número
usaremos este componente para habilitar nuestra aplicación de Búsqueda
de Archivos para buscar ficheros dentro de archivos comprimidos.
Para seguir los pasos necesitan tener la última versión de esta aplica-
ción. Si son nuevos a este newsletter o si no lo han estado siguiendo
últimamente, pueden descargarla de aquí:
http://www.latiumsoftware.com/es/file.php?id=p04
También necesitan tener el componente TZipMaster y las DLLs correcta-
mente instalados. Pueden encontrar instrucciones en el último
newsletter:
http://www.latiumsoftware.com/es/pascal/0005.php
Bien, suficiente introducción. Vamos a trabajar. A propósito, en caso
que se pregunten ;) algunas líneas nuevas o modificadas han sido
marcadas con un asterisco ("*") para resaltarlas, y los puntos
suspensivos ("...") significan que el resto es igual.
1) Incluimos al archivo de recursos de mensajes del componente ZipMaster
en el archivo de programa:
program FindFile;
uses
...
{$R *.RES}
* {$R ZipMsgUS.RES}
begin
...
También agregamos un mensaje extra en el formulario principal:
{$IFDEF Spanish}
...
* cstrFileExists = '"%s" ya existe. ¿Sobreescribir?';
{$ELSE}
...
* cstrFileExists = '"%s" already exists. Overwrite?';
{$ENDIF}
Este es el mensaje que se mostrará al usuario cuando tengamos que
descomprimir un fichero en el directorio temporal y ya exista un
fichero con ese nombre.
2) Definimos algunas funciones nuevas en la unidad Common:
unit Common;
interface
* uses classes, windows;
...
* function FileNameMatchesFilespec(const FileName, Filespec: string):
* boolean;
* function GetAssociatedSmallIcon(const FileName: string): HICON;
* function GetTempDir: string;
* function GetWindowsDir: string;
* function GetSystemDir: string;
implementation
* uses sysutils, registry, shellapi, filectrl;
...
// ===================================================================
function FileNameMatchesFilespec(const FileName, Filespec: string):
boolean;
// Devuelve True si FileName (por ejemplo 'RESUME.DOC') coincide con
// la especificación de ficheros Filespec (por ejemplo 'R*.DO?').
var
WName, WExt, FName, FExt: string;
// -------------
function ExpressionMatch(const s1, s2: string): boolean;
var
i, n, n1, n2: integer;
p1, p2: pchar;
begin
n1 := Length(s1);
n2 := Length(s2);
if n1 < n2 then n := n1 else n := n2;
p1 := pchar(s1);
p2 := pchar(s2);
for i := 1 to n do begin
if p2^ = '*' then begin
Result := True;
exit;
end;
if (p2^ <> '?') and (p2^ <> p1^) then begin
Result := False;
exit;
end;
inc(p1); inc(p2);
end;
if n1 = n2 then
Result := True
else if n1 > n2 then
Result := False
else begin // n1 < n2
for i := n1 + 1 to n2 do begin
if (p2^ <> '*') and (p2 <> '?') then begin
Result := False;
exit;
end;
inc(p2);
end;
Result := True;
end;
end;
// -------------
begin
WName := AnsiUpperCase(ExtractFileName(Filespec));
WExt := ExtractFileExt(WName);
WName := Copy(WName, 1, Length(WName) - Length(WExt));
FName := AnsiUpperCase(ExtractFileName(FileName));
FExt := ExtractFileExt(FName);
FName := Copy(FName, 1, Length(FName) - Length(FExt));
if WName = '' then WName := '*';
if WExt = '' then WExt := '.*';
if FExt = '' then FExt := '.';
Result := ExpressionMatch(FName, WName) and ExpressionMatch(FExt,
WExt);
end;
// ===================================================================
function RCPos(c: char; const s: string): integer;
// Devuelve la posición de la última ocurrencia (o la primera de
// derecha a izquierda) de un caracter en una cadena
var
i: integer;
p: pchar;
begin
i := Length(s);
p := pchar(s) + i - 1;
for i := i downto 1 do begin
if p^ = c then begin
Result := i;
exit;
end;
dec(p);
end;
Result := 0;
end;
// ===================================================================
function GetTempDir: string;
// Devuelve el directorio temporal de Windows
var
TmpDir: array [0..MAX_PATH-1] of char;
begin
SetString(Result, TmpDir, GetTempPath(MAX_PATH, TmpDir));
Result := ExcludeTrailingBackslash(Result);
if not DirectoryExists(Result) then begin
Result := GetWindowsDir + '\TEMP';
if not DirectoryExists(Result) then
try MkDir(Result);
except
Result := ExtractFileDrive(Result) + '\TEMP';
if not DirectoryExists(Result) then
try MkDir(Result);
except
Result := ExtractFileDrive(Result) + '\TMP';
if not DirectoryExists(Result) then
try MkDir(Result);
except
Result := '';
end;
end;
end;
end;
end;
// ===================================================================
function GetWindowsDir: string;
// Devuelve el directorio de Windows
var
WinDir: array [0..MAX_PATH-1] of char;
begin
SetString(Result, WinDir, GetWindowsDirectory(WinDir, MAX_PATH));
end;
// ===================================================================
function GetSystemDir: string;
// Devuelve el directorio System de Windows
var
SysDir: array [0..MAX_PATH-1] of char;
begin
SetString(Result, SysDir, GetSystemDirectory(SysDir, MAX_PATH));
end;
// ===================================================================
function GetAssociatedSmallIcon(const FileName: string): HICON;
// Devuelve el icono pequeño de un fiechero o documento dado, ya sea
// que realmente exista en el disco o no.
var
IconIndex: word;
SmallIconHandle: HIcon;
FileExt, FileType, IconSource: String;
p: integer;
Reg: TRegistry;
PLargeIconHandle: ^HIcon;
begin
FileExt := UpperCase(ExtractFileExt(FileName));
IconIndex := 0;
if ((FileExt = '.EXE') or (FileExt = '.ICO')) and
FileExists(FileName) then begin
IconSource := FileName;
end else begin
if FileExt = '.EXE' then FileExt := '.COM';
FileType := '';
IconSource := '';
Reg := TRegistry.Create(KEY_QUERY_VALUE);
Reg.RootKey := HKEY_CLASSES_ROOT;
if Reg.OpenKeyReadOnly(FileExt) then begin
FileType := Reg.ReadString('');
Reg.CloseKey;
end; // if
if FileType <> '' then begin
if Reg.OpenKeyReadOnly(FileType + '\DefaultIcon')
then begin
IconSource := Reg.ReadString('');
Reg.CloseKey;
end;
end;
Reg.Free;
if IconSource = '' then begin
IconSource := GetSystemDir + '\SHELL32.DLL';
if FileExt = '.DLL' then IconIndex := 66;
end else begin
p := RCPos(',', IconSource);
if p <> 0 then begin
IconIndex := StrToInt(Copy(IconSource, p + 1,
Length(IconSource) - p));
IconSource := Copy(IconSource, 1, p - 1);
end;
end;
end;
PLargeIconHandle := nil;
if ExtractIconEx(pchar(IconSource), IconIndex,
PLargeIconHandle^, SmallIconHandle, 1) <> 1 then begin
IconSource := GetSystemDir + '\SHELL32.DLL';
if FileExt = '.EXE' then IconIndex := 2
else if FileExt = '.COM' then IconIndex := 2
else if FileExt = '.INI' then IconIndex := 63
else if FileExt = '.INF' then IconIndex := 63
else if FileExt = '.BAT' then IconIndex := 65
else if FileExt = '.DLL' then IconIndex := 66
else IconIndex := 0;
if ExtractIconEx(pchar(IconSource), IconIndex,
PLargeIconHandle^, SmallIconHandle, 1) <> 1 then
Result := 0
else
Result := SmallIconHandle;
end else
Result := SmallIconHandle;
end;
// ===================================================================
end.
3) Agregamos dos componentes ZipMaster al formulario y los llamamos Zip1
y Zip2. Usaremos uno para el "hilo" (thread) de búsqueda y el otro para
descomprimir un fichero en el directorio temporal cuando el usuario le
haga doble-clic, así podemos abrirlo con su aplicación asociada.
4) Agregamos una casilla de verificación (CheckBox) así los usuarios
pueden especificar si quieren escanear dentro de archivos Zip o no. La
llamamos "chkScanZIPs" y la etiquetamos como "Revisar archivos &ZIP".
Agregamos código para habilitar esta nueva casilla de verificación
cuando sea apropiado:
procedure TForm1.Button1Click(Sender: TObject);
var
c: char;
begin
...
Checkbox1.Enabled := False;
* chkScanZIPs.Enabled := False;
Button2.Enabled := True;
...
end;
procedure TForm1.Thread1Done(var AMessage: TMessage);
begin
...
Checkbox1.Enabled := True;
* chkScanZIPs.Enabled := True;
...
end;
5) Agregamos código en TThread1.Execute para cargar la DLL Unzip (si se
necesita) antes de la búsqueda.
begin // procedure TThread1.Execute;
Count := 0;
Synchronize(Initialize);
Keywords := TStringList.Create;
GetKeywords(Keywords, OwnerForm.Edit2.Text);
n := Keywords.Count - 1;
for i := 0 to n do
Keywords[i] := UpperCase(Keywords[i]);
* if OwnerForm.chkScanZIPs.Checked then
* OwnerForm.Zip1.Load_Unz_Dll;
ScanFolder(OwnerForm.Edit3.Text);
Keywords.Free;
Synchronize(Finalize);
end;
Load_Unz_Dll no hará nada si la DLL ya está cargada. La DLL será automá-
ticamente descargada cuando el componente sea destruido (ocurrirá cuando
el formulario sea destruido).
6) Agregamos un bloque condicional en el procedimiento ScanFolder para
evitar cargar y escanear archivos ZIP dado que no son como los archivos
ordinarios y los trataremos separadamente. También agregamos una línea
para liberar la memoria ocupada por la cadena "Content" una vez que no
la necesitamos más.
procedure ScanFolder(const folder: string);
var
SearchRec: TSearchRec;
i: integer;
begin
if FindFirst(folder + OwnerForm.Edit1.Text,
faReadOnly Or faHidden Or faSysFile Or faArchive,
SearchRec) = 0 then begin
Location := folder;
repeat
try
FileName := SearchRec.Name;
* if UpperCase(ExtractFileExt(FileName)) <> '.ZIP' then begin
Content := UpperCase(LoadFile(folder + FileName));
Score := 0;
for i := 0 to n do
if Pos(Keywords[i], Content) <> 0 then inc(Score);
* Content := ''; // Libera la memoria ocupada
if Score > 0 then begin
inc(Count);
Time := FileDateToDateTime(SearchRec.Time);
Synchronize(AddFileName);
end; // if
* end; // if
except
end; // try
until Terminated Or (FindNext(SearchRec) <> 0);
end; // if
FindClose(SearchRec);
7) Agregamos el siguiente código inmediatamente después del que acabamos
de reproducir arriba:
if OwnerForm.chkScanZIPs.Checked then begin
if FindFirst(folder + '*.ZIP', faReadOnly Or faHidden Or faSysFile
Or faArchive, SearchRec) = 0 then begin
repeat
try
ScanZip(folder + SearchRec.Name);
except
end; // try
until Terminated Or (FindNext(SearchRec) <> 0);
end; // if
FindClose(SearchRec);
end;
Este código busca todos los archivos .ZIP en el directorio (referenciado
por la variable "folder") y llama a ScanZip para procesarlos.
8) Definimos el procedimiento ScanZip dentro del procedimiento
TThread1.Execute, justo antes de ScanFolder.
procedure TThread1.Execute;
var
...
// -------------------------------------
procedure ScanZip(const ZipName: string);
var
i, j: integer;
PZipDirEntry: ^ZipDirEntry;
ZipStream: TZipStream;
begin
OwnerForm.Zip1.ZipFileName := ZipName;
for j := 0 to OwnerForm.Zip1.Count - 1 do begin
try
PZipDirEntry := OwnerForm.Zip1.ZipContents[j];
if UpperCase(ExtractFileExt(PZipDirEntry.FileName)) = '.ZIP'
then begin
// A Zip inside a Zip. Skip it.
end else if PZipDirEntry.UncompressedSize > 0 then begin
FileName := ExtractFileName(PZipDirEntry.FileName);
if FileNameMatchesFilespec(FileName, OwnerForm.Edit1.Text)
then begin
ZipStream := OwnerForm.Zip1.ExtractFileToStream(
PZipDirEntry.FileName);
SetString(Content, PChar(ZipStream.Memory), ZipStream.Size);
ZipStream.Clear;
Content := UpperCase(Content);
Score := 0;
for i := 0 to n do
if Pos(Keywords[i], Content) <> 0 then inc(Score);
Content := ''; // Libera la memoria ocupada
if Score > 0 then begin
inc(Count);
Location := ZipName + '?'
+ ExtractFilePath(PZipDirEntry.FileName);
Time := FileDateToDateTime(PZipDirEntry.DateTime);
Synchronize(AddFileName);
end; // if
end; // if
end;
finally
end;
if Terminated then break;
end; // for j
end;
// -------------------------------------
procedure ScanFolder(const folder: string);
...
Lo que este procedimiento hace es primero abrir el archivo ZIP y luego
procesar todos sus ficheros en un bucle for..do. Si el fichero dentro
del archivo es un archivo ZIP o si su longitud es 0, o si no concuerda
con la especificación de ficheros, simplemente lo ignoramos.
Decidimos descomprimir los ficheros en memoria, usando el método
ExtractFileToStream que devuelve una corriente (Stream, una descendiente
de TMemoryStream para ser precisos). Luego ponemos el contenido de esta
corriente en una cadena y buscamos las palabras clave como hicimos con
los ficheros normales una vez que estaban cargados en una cadena. Si
encontramos alguna coincidencia, en vez de solamente el directorio,
establecemos "Location" como el camino y nombre completo del archivo
ZIP más un signo de interrogación (para señalizar que se trata de un
archivo ZIP) y el camino relativo del fichero dentro del archivo. Por
ejemplo C:\ZIPARCH.ZIP?VCL\ significa que la ubicación del fichero es
el directorio VCL dentro del archivo C:\ZIPARCH.ZIP.
9) La API ExtractIcon sólo funciona con ficheros que están en el disco,
pero ahora éste no será siempre el caso porque algunos ficheros estarán
dentro de un archivo, así que modificamos TThread1.AddFileName para usar
la función GetAssociatedSmallIcon que habíamos escrito en la unidad
Common.
procedure TThread1.AddFileName; // Synchronized
var
* ListItem: TListItem;
* Icon: TIcon;
* IconHandle: HIcon;
begin
...
ListItem.Caption := FileName;
* IconHandle := GetAssociatedSmallIcon(Location + FileName);
* Icon := TIcon.Create;
* if IconHandle <> 0 then Icon.Handle := IconHandle;
* ListItem.ImageIndex := OwnerForm.ImageList1.AddIcon(Icon);
* Icon.Free;
ListItem.SubItems.Add(Location);
...
end;
10) Editamos la propiedad Items de PopupMenu1 y le agregamos un nuevo
elemento llamado OpenArchive1, lo etiquetamos como "Abrir Archivo"
(propiedad Caption), y luego generamos su evento Click:
* procedure TForm1.OpenArchive1Click(Sender: TObject);
* begin
* OpenArchive(SelectedItem);
* end;
OpenArchive es un nuevo método que declaramos en las declaraciones
privadas del formulario, junto con otros nuevos métodos:
TForm1 = class(TForm)
...
private
{ Private declarations }
...
* procedure ExecuteAssociation(const FileName: string);
* procedure OpenFolder(ListItem: TListItem);
* procedure OpenFile(ListItem: TListItem);
* procedure OpenArchive(ListItem: TListItem);
public
{ Public declarations }
end;
Definimos estos métodos en la sección "implementation" como sigue:
procedure TForm1.ExecuteAssociation(const FileName: string);
begin
if ShellExecute(Self.Handle, nil, PChar(FileName),
nil, nil, SW_SHOWMAXIMIZED) <= 32 then
Application.MessageBox(cstrCouldNotExecApp,
'Error', MB_ICONEXCLAMATION);
end;
// -------------------------------------------------------------------
procedure TForm1.OpenFolder(ListItem: TListItem);
var
p: integer;
Folder: string;
begin
Folder := ListItem.SubItems.Strings[0];
p := Pos('?', Folder);
if p <> 0 then begin
SetLength(Folder, p-1);
Folder := ExtractFilePath(Folder);
end;
ExecuteAssociation(Folder);
end;
// -------------------------------------------------------------------
procedure TForm1.OpenFile(ListItem: TListItem);
var
p: integer;
Folder, FileName, CurrentDir: string;
begin
Folder := ListItem.SubItems.Strings[0];
p := Pos('?', Folder);
if p = 0 then
FileName := Folder + ListItem.Caption
else begin // Compressed file
FileName := GetTempDir + '\' + ListItem.Caption;
if FileExists(FileName) then
if Application.MessageBox(PChar(Format(cstrFileExists,
[FileName])), 'Warning', MB_ICONQUESTION or MB_YESNO)
= IDNO then
exit;
Zip2.Load_Unz_Dll;
Zip2.ZipFileName := Copy(Folder, 1, p - 1);
Zip2.FSpecArgs.Add(Copy(Folder, p + 1, Length(Folder) - p)
+ ListItem.Caption);
Zip2.ExtrOptions := [ExtrOverWrite];
CurrentDir := GetCurrentDir;
ChDir(ExtractFilePath(FileName));
try
Zip2.Extract;
finally
ChDir(CurrentDir);
end;
if not FileExists(FileName) then exit;
end;
ExecuteAssociation(FileName);
end;
// -------------------------------------------------------------------
procedure TForm1.OpenArchive(ListItem: TListItem);
var
Archive: string;
begin
Archive := ListItem.SubItems.Strings[0];
SetLength(Archive, Pos('?', Archive) - 1);
ExecuteAssociation(Archive);
end;
// -------------------------------------------------------------------
11) Modificamos TForm1.ListView1DblClick, TForm1.Open1Click y
TForm1.OpenFolder1Click para llamar a estos nuevos métodos en vez de
manejar las cosas por ellos mismos:
procedure TForm1.ListView1DblClick(Sender: TObject);
var
Col: Integer;
ListItem: TListItem;
begin
ListItem := TListViewX(ListView1).GetItemAtX(Last.X, Last.Y, Col);
* if ListItem <> nil then
* if Col = 0 then
* OpenFile(ListItem)
* else if Col = 1 then
* if Pos('?', ListItem.SubItems.Strings[0]) <> 0 then
* OpenArchive(ListItem)
* else
* OpenFolder(ListItem);
end;
...
procedure TForm1.Open1Click(Sender: TObject);
begin
* OpenFile(SelectedItem);
end;
// -------------------------------------------------------------------
procedure TForm1.OpenFolder1Click(Sender: TObject);
begin
* OpenFolder(SelectedItem);
end;
11) Modificamos TForm1.ListView1MouseDown y TForm1.ListView1KeyDown para
habilitar/inhabilitar el nuevo elemento del menú antes de invocar el
menú contextual:
procedure TForm1.ListView1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
...
if (SelectedItem <> nil) and (Col <= 1) then begin
* PopupMenu1.Items[2].Enabled :=
* Pos('?', SelectedItem.SubItems[0]) <> 0;
* if PopupMenu1.Items[2].Enabled then
* PopupMenu1.Items[2].Default := True
* else
* PopupMenu1.Items[Col].Default := True;
PopupMenu1.Popup(
...
procedure TForm1.ListView1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
...
if SelectedItem <> nil then begin
* PopupMenu1.Items[2].Enabled :=
* Pos('?', SelectedItem.SubItems[0]) <> 0;
PopupMenu1.Items[0].Default := True;
PopupMenu1.Popup(
...
Ok, eso debería ser todo. Ya puede intentar ejecutar el ejemplo. Si es
muy vago para hacer todos los cambios por usted mismo ;) o si tiene
algún problema :( puede descargar el código fuente completo:
http://www.latiumsoftware.com/es/file.php?id=p06
________________________________________________________________________
4. EL NOVATO Y EL EXPERTO
Constantes, variables y expresiones lógicas (booleanas)
En esta edición hemos decidido comenzar esta nueva sección que apunta a
retratar las diferencias entre los estilos de programación de novatos y
programadores expertos. Si tiene más ejemplos como los de abajo, por
favor compártalos con nosotros para que podamos publicarlos.
Hemos decidido comenzar con expresiones booleanas dado que quizás
marquen las más notorias diferencias. En los siguientes ejemplos, se
supone que "b" es una variables booleana (de tipo boolean)...
NOVATO:
if a[i] = x then
b := True
else
b := False;
EXPERTO:
b := a[i] = x;
Explicación: "a[i] = x" es una expresión de comparación, y como tal es
una expresión booleana, es decir, se evalúa como True o False (verdadero
o falso), así que podemos asignar el resultado de la expresión directa-
mente a nuestra variable booleana. Por ejemplo, si "a[i] = x" es True
(verdadero), éste es el valor que será asignado a "b", y si es False
(falso), a "b" se le asignará False. ¿Correcto?
--------------
NOVATO:
if a[i] = x then
b := False
else
b := True;
EXPERTO:
b := a[i] <> x;
Explicación: Este es similar al ejemplo anterior, pero en este caso
invertimos el operador de comparación para invertir el resultado. Por
ejemplo, si "a[i] = x" es True, "a[i] <> x" será False, y éste es el
valor asignado a "b", y si "a[i] = x" es False, "a[i] <> x" será True
y éste es el valor que se asigna a "b".
En lugar de "a[i] <> x" podríamos haber escrito "not (a[i] = x)", pero
hace la expresión más compleja puesto que usa un operador más, y además
es un poco más difícil de leer.
--------------
NOVATO:
if b = True then c = '*';
EXPERTO:
if b then c = '*';
Explicación: El "if" ya de por sí evalúa si la condición es True, así
que nosotros no tenemos que hacerlo.
--------------
NOVATO:
if b = False then c = '*';
EXPERTO:
if not b then c = '*';
Explicación: Ahora necesitamos actuar en la falsedad de la condición,
pero en lugar de preguntar si es False, podemos invertir la condición
con un "not", así que estaríamos preguntando si no es True, en su lugar.
--------------
Tenemos que decir que en cada uno de los ejemplos que acabamos de
presentar arriba, ambas formas de programación son correctas y en teoría
el compilador de Delphi es lo suficientemente inteligente como para
producir idéntico código de máquina. La única diferencia es que los
profesionales escriben menos y muestran que entienden las expresiones
booleanas...
________________________________________________________________________
Si no has recibido el archivo con el código fuente completo de los
ejemplos que se presentan en este boletín, puedes descargarlo de la
siguiente dirección: http://www.latiumsoftware.com/es/file.php?id=p06
________________________________________________________________________
Página principal: http://www.latiumsoftware.com/es/pascal/index.php
Página del grupo: http://espanol.groups.yahoo.com/group/boletin-pascal/
Para suscribirse / apuntarse: boletin-pascal-subscribe@gruposyahoo.com
Para cancelar / removerse: boletin-pascal-unsubscribe@gruposyahoo.com
Para reportar problemas con la suscripción: eds2008 @ latiumsoftware.com
________________________________________________________________________
Este boletín se provee "TAL Y COMO ESTA", sin garantía de ninguna clase.
Su uso implica la aceptación de nuestros términos de licencia y de la
ausencia de garantía que puedes leer en nuestro sitio web. Allí también
encontrarás una nota sobre marcas registradas. Te animamos a que redis-
tribuyas este boletín, siempre y cuando lo hagas en forma completa
(incluyendo la información de copyright), sin modificaciones y de manera
gratuita. Los artículos son copyright de sus respectivos autores y se
reproducen aquí con el permiso de los mismos.
________________________________________________________________________
Latium Software http://www.latiumsoftware.com/es/index.php
Copyright (c) 2000 por Ernesto De Spirito. Todos los derechos reservados
________________________________________________________________________
|