Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

viernes, 29 de septiembre de 2023

Hilos con C# (C Sharp)

Otro tutorial viejito, muy viejito, hay un detalle qué arreglar en el código, pero eso lo voy a explicar en una siguiente entrada:

¡Hilos! Visual Basic 6 no los permite, y programarlos en Borland C++ es muy engorroso (personalmente odio tener que estar programando archivos .h y el Borland los necesita para poder usar los hilos). En este tutorial voy a usar C#.

Para el programita que voy a mostrar a continuación hay que abrir el Visual C# (yo uso el 2005), crear un nuevo proyecto tipo Windows Application y en el formulario que aparece, ponerle dos Pictureboxes: pictureBox1 y pictureBox2. Al final, debe verse así:

 

Y luego se le mete este código:

using System;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Threading;

 

namespace WindowsApplication1

{

    public partial class Form1 : Form

    {

        Thread p1;

        Thread p2;

        byte r, g;

        bool b1, b2;

       

        public Form1()

        {

            InitializeComponent();

        }

 

        private void Form1_Load(object sender, EventArgs e)

        {

            r = 0; g = 255; b1 = false; b2 = true;

            p1 = new Thread(new ThreadStart(Hilo1));

            p2 = new Thread(new ThreadStart(Hilo2));

            p1.Start();

            p2.Start();

        }

       

        public void Hilo1() {

            while (true)

            {

                Thread.Sleep(10);

 

                if (r >= 0 && r <= 255 && b1 == false)

                {

                    r++;

                    if (r == 255)

                        b1 = true;                   

                }

 

                if (r >= 0 && r <= 255 && b1 == true)

                {

                    r--;

                    if (r == 0)

                        b1 = false;

                }

                pictureBox1.BackColor = Color.FromArgb(r, 80, 100);

            }

       }

 

        public void Hilo2() {

            while (true)

            {

                Thread.Sleep(10);

 

                if (g >= 0 && g <= 255 && b2 == false)

                {

                    g++;

 

                    if (g == 255)

                        b2 = true;

                }

 

                if (g >= 0 && g <= 255 && b2 == true)

                {

                    g--;

 

                    if (g == 0)

                        b2 = false;

                }     

                pictureBox2.BackColor = Color.FromArgb(100, g, 80); 

            }                  

        }

 

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)

        {

            p1.Abort();

            p2.Abort();

        }

    }

}

 

Lo que se debe entender primero es que existen dos hilos: "p1" y "p2" (o dos objetos de la clase "Thread", en inglés "Hilo") cada uno asociado a un método: A "p1" le corresponde "Hilo1" y a "p2" le corresponde "Hilo2". Lo que "p1" y "p2" permiten es que ambos métodos (Hilo1 e Hilo2) se ejecuten de manera simultánea.

Todas las otras variables en el programa son de control. Los hilos empiezan a ejecutarse al llamar al método "Start" y se terminan con "Abort". El C# no permite llamar de nuevo a "Start" para un hilo que ya se está ejecutando.

Lo que hace el programa simplemente es variar el color de ambos PictureBoxes en un bucle que jamás termina, para ello se usa el comando CualquierPictureBox.BackColor = Color.FromArgb(100, g, 80);

También uso el método Thread.Sleep que detiene la ejecución del hilo durante el tiempo en milisegundos que va entre paréntesis. Esto es para que los colores varíen despacio y se puedan apreciar.

Cada vez que dentro de un método se usa la palabra "Thread" ésta se refiere al hilo que está asociado a él.

Una buena página para aprender hilos en C# es ésta: https://www.albahari.com/

Y acá se puede descargar mi ejemplo de los PictureBoxes.

sábado, 17 de junio de 2023

Cuadrados recursivos (y con colores) usando la tortuga de Python

Quise jugar un poco más con la recursividad en Python, así que cogí mi código de los arbolitos fractales y lo modifiqué para que dibujara cuadraditos (tuve que hacer unos cálculos en mi cabeza para que los cuadrados pequeños se ubicaran en medio de cada lado del cuadrado más grande):

from operator import length_hint
import turtle as t

def cuadrado(lenght):
    t.forward(lenght)
    t.seth(90)
    t.forward(lenght)
    t.seth(180)
    t.forward(lenght)
    t.seth(270)
    t.forward(lenght)
    t.seth(0)

def drawit2(x, y, lenght):
    if lenght > 5:
        t.penup()
        t.goto(x, y)
        
        if lenght < 20:
            t.color('gray')
        else:
            t.color('black')

        t.pensize(lenght/15)
        t.pendown()
        cuadrado(lenght)
                
        cx = x - lenght / 4
        cy = y + lenght / 4
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght / 4
        cy = y - lenght / 4
        
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght * 3 / 4
        cy = y + lenght / 4
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght / 4
        cy = y + lenght * 3 / 4
        drawit2(cx, cy, lenght / 2)
    else:
        t.hideturtle()
        return 0

lenght = 160
drawit2(0 - lenght / 2, 0 - lenght / 2, 160)
t.mainloop()


Creo que no es necesario el import length_hint, (no recuerdo de qué tutorial lo copié) pero los dibujos tardan su buen rato en terminarse de dibujar, así que al final dejé el import.

El resultado es:



Se ve bien, pero no lo suficiente, hice un pequeño cambio al parámetro que se pasa a la función cuadrado():

from operator import length_hint
import turtle as t

def cuadrado(lenght):
    t.forward(lenght)
    t.seth(90)
    t.forward(lenght)
    t.seth(180)
    t.forward(lenght)
    t.seth(270)
    t.forward(lenght)
    t.seth(0)

def drawit2(x, y, lenght):
    if lenght > 5:
        t.penup()
        t.goto(x, y)
        
        if lenght < 20:
            t.color('gray')
        else:
            t.color('black')

        t.pensize(lenght/15)
        t.pendown()
        cuadrado(lenght - lenght / 10)
                
        cx = x - lenght / 4
        cy = y + lenght / 4
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght / 4
        cy = y - lenght / 4
        
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght * 3 / 4
        cy = y + lenght / 4
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght / 4
        cy = y + lenght * 3 / 4
        drawit2(cx, cy, lenght / 2)
    else:
        t.hideturtle()
        return 0

lenght = 160
drawit2(0 - lenght / 2, 0 - lenght / 2, 160)
t.mainloop()


El resultado es:


Mucho mejor, pero le falta color, el código para el color lo copié de aquí.


from operator import length_hint
import turtle as t
from turtle import*
from random import randint

def cuadrado(lenght):
    color(randint(0, 255), randint(0, 255), randint(0, 255))
    t.forward(lenght)
    t.seth(90)
    t.forward(lenght)
    t.seth(180)
    t.forward(lenght)
    t.seth(270)
    t.forward(lenght)
    t.seth(0)

def drawit2(x, y, lenght):
    if lenght > 5:
        t.penup()
        t.goto(x, y)
        
        t.pensize(lenght/15)
        t.pendown()
        cuadrado(lenght - lenght / 10)
                
        cx = x - lenght / 4
        cy = y + lenght / 4
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght / 4
        cy = y - lenght / 4
        
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght * 3 / 4
        cy = y + lenght / 4
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght / 4
        cy = y + lenght * 3 / 4
        drawit2(cx, cy, lenght / 2)
    else:
        t.hideturtle()
        return 0

lenght = 160
colormode(255)
drawit2(0 - lenght / 2, 0 - lenght / 2, 160)
t.mainloop()


El resultado es:


No está mal, pero esas líneas gruesas del cuadrado grande molestan un poco. Voy a ponder un color distinto a cada línea:

from operator import length_hint
import turtle as t
from turtle import*
from random import randint

def cuadrado(lenght):
    color(randint(0, 255), randint(0, 255), randint(0, 255))
    t.forward(lenght)
    t.seth(90)
    color(randint(0, 255), randint(0, 255), randint(0, 255))
    t.forward(lenght)
    t.seth(180)
    color(randint(0, 255), randint(0, 255), randint(0, 255))
    t.forward(lenght)
    t.seth(270)
    color(randint(0, 255), randint(0, 255), randint(0, 255))
    t.forward(lenght)
    t.seth(0)

def drawit2(x, y, lenght):
    if lenght > 5:
        t.penup()
        t.goto(x, y)
        
        t.pensize(lenght/15)
        t.pendown()
        cuadrado(lenght - lenght / 10)
                
        cx = x - lenght / 4
        cy = y + lenght / 4
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght / 4
        cy = y - lenght / 4
        
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght * 3 / 4
        cy = y + lenght / 4
        drawit2(cx, cy, lenght / 2)
        cx = x + lenght / 4
        cy = y + lenght * 3 / 4
        drawit2(cx, cy, lenght / 2)
    else:
        t.hideturtle()
        return 0

lenght = 160
colormode(255)
drawit2(0 - lenght / 2, 0 - lenght / 2, 160)
t.mainloop()


Queda asi:


Creo que prefiero la versión en negro y gris.

Como se ve tan feo, encontré un dato en uno de los tutoriales: el módulo colorsys. Con la ayuda de este enlace y luego de probar un poco llegué a este código:

from colorsys import hsv_to_rgb
import turtle as t
from turtle import*
from random import randint

def cuadrado(lenght, n):
    t.pencolor(hsv_to_rgb(n / 36, 0.75, 0.75))
    t.forward(lenght)
    t.seth(90)
    t.forward(lenght)
    t.seth(180)
    t.forward(lenght)
    t.seth(270)
    t.forward(lenght)
    t.seth(0)

def drawit2(x, y, lenght, n):
    if lenght > 5:
        t.penup()
        t.goto(x, y)
        n += 4
                
        t.pensize(lenght/15)
        t.pendown()
        cuadrado(lenght - lenght / 10, n)
                
        cx = x - lenght / 4
        cy = y + lenght / 4
        drawit2(cx, cy, lenght / 2, n + 1)
        cx = x + lenght / 4
        cy = y - lenght / 4
        
        
drawit2(cx, cy, lenght / 2, n + 2)
        cx = x + lenght * 3 / 4
        cy = y + lenght / 4
        
drawit2(cx, cy, lenght / 2, n + 3)
        cx = x + lenght / 4
        cy = y + lenght * 3 / 4
        
drawit2(cx, cy, lenght / 2, n + 4)
    else:
        t.hideturtle()
        return 0

lenght = 160
drawit2(0 - lenght / 2, 0 - lenght / 2, 160, 12)
t.mainloop()


Se ve mucho mejor ahora:


lunes, 24 de abril de 2023

Comandos de git para eliminar commits, y ramas locales y remotas

Intentaba hacer un git checkout otra_rama y me salió este error:

fatal: unable to create 'xxx/.git/index.lock': file exists.

Another git process seems to be running in this repository, e.g. an editor opened by 'git commit'. Please make sure all processes are terminated then try again. If it still fails, a git process may have crashed in this repository earlier: remove the file manually to continue.

Al ejecutarse, git deja un archivo dentro de la carpeta oculta .git llamad index.lock el cual puede eliminarse manualmente. En mi caso, una de las ventanas de consola había dejado de responder y tuve que matarla.

Pongo también el comando que se usa para eliminar el último commit:

git reset --hard HEAD~1

Si se quiere eliminar una rama local, se debe hacer:

git checkout otra_rama

git branch -D rama_que_quiero_eliminar

Lo que no dicen los tutoriales es que primero debemos ubicarnos en otra rama o se lanzará el error:

cannot delete branch checked out at xxx

Para eliminar una rama remota:

git push origin --delete rama_a_eliminar 

o también:

git push origin -d rama_a_eliminar 

Si se ingresa el comando:

git push --delete rama_a_eliminar 

Lanzará el error:

fatal: --delete desn't make any sense without any refs

Me pasé horas buscando cómo restaurar los refs en lugar de revisar el comando y darme cuenta que me faltaba el origin :(

martes, 28 de febrero de 2023

Tutorial viejito: Un Par de Trucos al hacer Guis en Matlab 7

Este tutorial data de mis años de estudiante hace casi dos décadas atrás, enjoy!


Matlab 7 permite poner imágenes en las ventanas hechas con la herramienta Guide, aunque no directamente pues no posee un control Picture o Image como en Visual Basic o C++ Builder. Lo malo es que los archivos de Ayuda de Matlab no explican cómo poner una imagen a una ventana (y si lo hacen yo no pude encontrar dónde), después de una tarde de examinar y diseccionar el Matlab descubrí la manera de hacerlo.

Y aquí viene lo que quiero explicar:

 
CÓMO PONER UNA IMAGEN EN UNA INTERFAZ GRÁFICA


Primero la imagen que queremos colocar en la ventana debe estar en formato BMP y en la misma carpeta donde se guardarán los archivos de la Interfaz Gráfica (el *.fig y el *.m).

Luego se debe crear una ventana en blanco, esto se hace escribiendo "Guide" en la ventana de comandos y presionando enter, entonces se abrirá la siguiente ventana:



Elegimos "Blank GUI" con lo que se abrirá esta ventana:



Después se jala un control Axes (el botón con la curva azul):



Mi ventana la he guardado como "unt1.fig" lo cual genera un archivo "unt1.m", ahora nos vamos para allá. Lo que debemos buscar es la función OpeningFcn que se activa apenas se abre la ventana:


Para el Matlab la ventana tiene por defecto el nombre de "figure1" y el Axes el de "axes1". Estos nombres pueden cambiarse, pero en este caso los vamos a usar así como están.

Lo que debe hacerse es añadir el siguiente código (no hay que hacerle caso a lo que está en verde, son comentarios y pueden borrarse):



Lo que hace este código es cargar la imagen (cuyo nombre es "dsp.bmp") a una matriz con la función "imread", por precaución se cambia a formato entero de 8 bits (aunque una imagen en BMP ya está en este formato).

La tercera línea hace que la imagen dependa de axes1. Las siguientes dos líneas son para configurar axes1: la propiedad "Visible" debe estar en "off" o de otro modo se verán los ejes coordenados a los costados de la imagen.

Lo demás es para que la imagen tenga el mismo tamaño que axes1. Los tres puntos simplemente son para indicarle al Matlab que la función continúa en la siguiente línea.

Al correr nuestra ventana debe verse así:



El ancho y alto de la imagen puede variarse variando el ancho y alto de axes1.

 

CÓMO HACER LA PARTE MÁS OSCURA PARA EL TÍTULO:

Para esto se deben jalar un control tipo "panel" y uno tipo "static text". A ambos se les da click derecho y se escoge "Send to back", lo cual los pondrá detrás de la imagen y no la taparán:


Al hacer doble click sobre cualquiera de ellos aparecerá la ventana de propiedades con las respectivas propiedades que pueden usarse para cada control. Por ejemplo, el panel y el texto estático tienen una propiedad llamada "BackgroundColor", la cual no tiene figure1, pero ésta posee la propiedad "Color". Es importante saber esto, pues no todos los controles tienen las mismas propiedades.

El panel tiene por defecto el nombre de "uipanel1" y el texto estático el de "text1".

Nos volvemos a ir a la función OpeningFcn y añadimos (sí, borré los comentarios del Matlab 7):



El color de la ventana por defecto es el que le da el sistema operativo. Si queremos que la ventana tenga el color que le asigna el windows y no uno fijo, y que el recuadro donde irá el título sea un poco más oscuro, es necesario tener los valores del color asignado (son 3, para rojo, verde y azul). Estos valores se cargan a un vector llamado "cc".

Para oscurecerlo un poco se le resta un número entre 0 y 1 al vector. El valor resultante se asigna a la propiedad "BackgroundColor" de text1 y uipanel1.

El resultado es éste:


Y con otras propiedades de pantalla (en Windows 2000, este es un tutorial MUY viejo):



Se le pueden arreglar algunas propiedades, como el tamaño y color de la letra de text1 y borrar el título de uipanel1 si se desea:


(no podía dejar mi sistema con ese amarillo tan molesto!)

viernes, 6 de enero de 2023

Resolviendo el Problema 108 de ProjectEuler.net

Otra entrada migrada de la vieja web:

Visitando la página de ProjectEuler.Net escogí intentar resolver el problema 108, el cual dice:


Hay que hallar el mínimo valor de n para el que existen al menos 1000 soluciones a la ecuación 1/x + 1/y = 1/n, donde "x", "y", "n" son números naturales.

Parecía que la solución estaba en simplemente meter x e y en dos bucles anidados y evaluar la ecuación para distintos valores de "n" y, junto con un contador, romper los bucles cuando éste exceda mil. Mas el primer problema que hallé es cómo encontrar los límites superiores de los bucles ¿cómo sé que no debo buscar en la infinita totalidad del conjunto de números naturales? En el problema se entiende que la solución existe y es finita.

Después de jugar con la ecuación, despejar variables, quitar denominadores, iguala a cero... y finalmente frustrarme, un foro me dio una pista: la solución de los límites para "x" e "y" está en la ecuación en su forma original.

Entonces me puse a analizarla y a meditarla...



Entonces (si se ignoran todos los dibujitos) salta a la vista que "x" e "y" deben ser mayores que "n" para cumplir la condición que todas las variables son números naturales. El valor de n+1 es el límite inferior de los bucles.

Si hago x = n+1:

Si el valor mínimo de "x" e "y" es n+1, el valor máximo será n^2+n, ya que para cumplir que 1/x + 1/y = 1/n, si "x" tiene un valor máximo, "y" tendrá un valor mínimo. En una fracción, mientras más grande es el denominador, más pequeña será la fracción, y viceversa.

Mi primer intento de resolver el problema 108 tenía dos bucles anidados que evaluaban los valores de "x" e "y", dentro de un bucle while, el cual se rompía cuando un contador superaba el valor de 1000, mientras no lo hiciera incrementaba el valor de "n". Mirando la ecuación también se deduce que "n" debe ser mayor a 32, ya que n^2+n - (n+1) = 31.606, redondeando: 32.

Pero este primer intento era mortalmente lento.

Luego de correr el programita para valores pequeños de "n", noté que las soluciones se repetían cuando "x" e "y" eran iguales a 2*n. Esto se entiende por la propiedad conmutativa de la suma:

El siguiente valor entero es 2n + 1, donde "z" puede ser "x" ó "y", pues los valores se repiten para las dos variables:


Se concluye que se puede decir que "x" va desde n+1 hasta 2n, "y" va desde 2*n+1 hasta n^2+n.

Para que se cumpla la condición de las mil soluciones, "x" debe tener al menos mil valores, es decir: 2n - (n+1) >1000. El valor mínimo para "n" es 999. En el programa empieza desde 1000:


 

Pero esta solución también tardaba siglos.

Entonces, jugando un poco con la ecuación, llegué a esto:


 

Para el problema 108, no interesan los valores de "n", "x" e "y", sólo si la relación xy/(x+y) da mil soluciones enteras. Evalúo los posibles valores de "x" e "y" para el intervalo [n+1, 2n], dentro de dos bucles: 


Queda mucho mejor y da la solución al problema en menos de un minuto, corriendo como código administrado sobre .Net 3.5 :D

Mi programita se veía interesante y bonito... tan bonito que me dije ¿por qué no pasarlo a Linq?

 

 

Dado que el compilador debe hacer varias conversiones si se usa Linq, esta última solución es unas tres veces más lenta.

El código fuente puee descargarse de aquí.

 

martes, 29 de noviembre de 2022

Accediendo a una base de datos en Visual Basic 6

Este tutorial es viejo viejo, de cuando recién aprendía a hacer conexiones a bases de datos con el VB6 en aquellos fines de los 90s/inicios de los 2000s. Lo dejo aquí como parte de la historia de la informatica.

La coneja no tiene nada que ver con el tutorial, pero creo que es un bonito separador :)

Empezamos:


Sucede que quienes programamos, en algún momento, tenemos que toparnos con las bases de datos queramos o no. Lo malo es que éstas son un mundo muy diferente a los punteros, al manejo de puertos, a la captura de señales o a las llamadas a Apis. Lo bueno es que una vez que se domina lo básico son lo más fácil del mundo. Y lo mejor es que los lenguajes de programación de alto nivel ya vienen con herramientas para manejas bases de datos de todo tipo.

Y eso es lo que quiero mostrar aquí: lo básico para manejar una pequeña base de datos en Access 2002 en el lenguaje de programación más fácil de entender de la Historia: Visual Basic 6.

Antes que nada hay que cargar el componente ADO (Click derecho en la barra de herramientas -> Componentes -> Microsoft ADO Data Control 6.0) y ponerlo en el formulario. Luego se guarda y se borra el componente ADO y, si se quiere, la referencia en la barra de herramientas. Esto es para que el Visual Basic reconozca los objetos Recordset y Connection.

El programa (con su código fuente) se pueden bajar de aquí. 

 


Y ahora una explicación:

Los datos a agregar o eliminar se ingresan en Text1 y Text2. La navegación se hace con los botones Anterior y Siguiente (Command3 y Command4). El botón Limpiar (Command2) es sólo para borrar lo escrito en Text1 y Text2. En el control List1 aparecen todos los registros que coinciden con la búsqueda del texto en Text3 realizada al oprimir "Buscar Todos" (Command7), en cambio el botón "Buscar" (Command5) sólo hace aparecer el primer resultado encontrado.

Para manejar la base de datos no es necesario añadir ningún control especial, basta declarar dos objetos:

Dim rs As Recordset
Dim db As Connection


"db" realiza la conexión con la base de datos y "rs" permitirá recorrer los resgistros, borrarlos, añadirlos y buscarlos. Para conextar con la base de datos (llamada "bd1.mdb") bastan estas líneas:

db.Open "PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source=" & pathBD & ";"
rs.Open "select * from Datos", db, adOpenDynamic, adLockOptimistic


bd1.mdb sólo tiene dos tablas: "Datos" y "datos2". El programa sólo trabaja con "Datos". El programador debe saber qué tablas y qué campos tiene su base de datos para poder trabajar con ella.

Es importante saber también en qué programa y en qué versión está hecha la base de datos, sino no podrá conectar. Para versiones anteriores de Access se pone:

db.Open "PROVIDER=Microsoft.Jet.OLEDB.3.51;Data Source=" & pathBD & ";"

Aquí hay que observar el primer parámetro que se le pasa a rs.Open: "select * from Datos" esto significa que rs dee recibir todos los registros de la tabla Datos de la base de datos.

"select" es un comando de los Procedimientos Almacenados de las bases de datos. Los Procedimientos Almacenados son órdenes que éstas interpretan y ejecutan y se basan en la lógica booleana. Así, si yo quisiera todos los registros cuyos Nombres coinciden con lo que se ingresa en Text3 (una búsqueda) basta poner:

rs.Close
rs.Open "select * from Datos where Nombre = '" & Text3.Text & "'", db, adOpenDynamic, adLockOptimistic


Para buscar todos los registros que coincidan con Text1 ó con Text2 (Nombre o Apellido) se pondría:

rs.Open "select * from Datos where Nombre = '" & Text1.Text & "' or Apellido = '" & Text2.Text & "'", db, adOpenDynamic, adLockOptimistic


(hay que tener cuidado de que el contenido de Text1 y Text2, o las condiciones de búsqueda si son de tipo String, estén entre comillas simples).

Como rs se vuelve a abrir, es necesario cerrarlo previamente.

Otro comando es "delete", cuando se lo usa el objeto rs se cierra después de la ejecución porllo que ya no es necesario poner rs.Close. Pero para que se pueda seguir navegando entre todos los registros de la tabla Datos se debe vovler a abrir.

rs.Open "delete * from Datos where Nombre = '" & Text1.Text & "' and Apellido = '" & Text2.Text & "'", db, adOpenDynamic, adLockOptimistic


Esta orden borra los registros cuyo Nombre y Apellido coincidan con los textos en Text1 y Text2.

Los procedimientos almacenados permiten realizar muchas cosas en la base de datos. Por ejemplo, imaginando que se tiene una tabla "Empleados" con campos "Edad" que contiene data numérica, "ID" y "Profesion". Luego de hacer las conexiones respectivas, para obtener a todos los ID y las profesiones correspondientes a mayores de edad se puede poner:

select ID, Profesion from Empleados where Edad > 18

Para agregar un registro basta una función propia de rs: AddNew y luego especificar los campos con la función "Fields" a donde se añadirán los nuevos datos. Al recorrer los registros de atrás o hacia adelante (propiedades MoveNext, MovePrevious, MoveLast y MoveFirst), o al hacer una búsqueda, "Fields" se coloca automáticamente en el registro actual.

Las propiedades de rs: EOF y BOF indican si se está al final o al principio de la tabla Datos. Mientras no se lo esté, sus valores son False. Tanto las búsquedas como los recorridos y los borrados de los registros en las tablas de la base de datos deben hacerse mientras EOF y BOF son False, si no nos "saldremos" de la base de datos y nos botará error.

Si en una búsqueda no se encuentra nada, EOF y BOF serán True.