Você está na página 1de 12

E Ed du ua ar rd do o O Ol la az z

Comencemos a programar con


VBA - Access


Entrega 10

Estructuras de Control II
10 - 2

eduardo@olaz.net E Ed du ua ar rd do o O Ol la az z

Estructuras de Control, segunda parte
Las Instrucciones While - - - Wend
La estructura de bucle
For Contador = ValorInicial To ValorFinal Step Salto
- -
Next Contador
que hemos analizado en la entrega anterior, realiza una iteracin del cdigo un nmero de
veces que resulta previsible en funcin de los valores ValorInicial, ValorFinal y
Salto.
En las sucesivas iteraciones, la variable Contador va tomando valores que varan de
forma constante entre un ciclo y otro.
El cdigo incluido en el bucle se ejecutar al menos una vez, aunque fuera de forma
incompleta si en su camino se tropezara con una sentencia Exit For.
Supongamos que necesitamos una estructura que se vaya ejecutando mientras el valor que
va tomando una variable cumpla determinadas caractersticas, y adems que esa variable
pueda cambiar en forma no lineal.
Para realizar esta tarea podemos contar con la clsica estructura While - - Wend.
Digo lo de clsica porque es un tipo de estructura que ha existido desde las primeras
versiones de Basic.
Esta estructura tiene la siguiente sintaxis
While condicin
[intrucciones]
Wend
Condicin es una expresin numrica o de tipo texto, que puede devolver True, False
Null. Si devolviera Null, While lo considerara como False.
Las instrucciones de cdigo se ejecutarn mientras condicin de cmo resultado
True.
Supongamos que queremos crear un procedimiento que nos muestre los sucesivos valores
que va tomando una variable, mientras esta variable sea menor que 100.
Los valores que ir tomando la variable sern cada vez el doble que la anterior.
Podramos realizarlo de esta forma
Public Sub PruebaWhile()
Dim lngControl As Long
lngControl = 1
While lngControl < 100
Debug.Print lngControl
lngControl = lngControl * 2
Wend
End Sub

Entrega 10 Estructuras de Control II 10 - 3


Comencemos a programar con VBA - Access

Este cdigo nos mostrar en la ventana inmediato:
1
2
4
8
16
32
64
Tras efectuar el 7 ciclo, la variable lngControl tomar el valor 128, por lo que la
expresin lngControl < 100 devolver False.
Esto har que el cdigo pase a la lnea siguiente a Wend, con lo que el procedimiento de
prueba finalizar.
Una utilizacin tradicional para While - - Wend ha sido la lectura de ficheros
secuenciales de texto, utilizando la funcin Eof, ficheros de los que de entrada no se
conoce el nmero de lneas,.
Esta funcin, mientras no se llega al final del fichero devuelve el valor False.
Cuando llega al final devuelve el valor True.
Por ello el valor Not Eof, mientras no se haya llegado al final del fichero, devolver lo
contrario, es decir True.
Veamos el siguiente cdigo:
Public Sub MuestraFichero( _
ByVal Fichero As String)
Dim intFichero As Integer
Dim strLinea As String

intFichero = FreeFile
Open Fichero For Input As #intFichero
While Not EOF(intFichero)
Line Input #intFichero, strLinea
Debug.Print strLinea
Wend
End Sub
ste es el clsico cdigo para leer el contenido de un fichero secuencial.
Vamos a fijarnos en la estructura While - - Wend.
Traducido a lenguaje humano quiere decir:
Mientras no llegues al final del fichero #intFichero
Lee la lnea del fichero, hasta que encuentres un retorno de carro y asgnaselo a la
variable strLinea.
Imprime el contenido de la variable en la ventana inmediato
Vuelve a la lnea de While para repetir el proceso
10 - 4

eduardo@olaz.net E Ed du ua ar rd do o O Ol la az z

Las Instrucciones Do - - - Loop
El conjunto de instrucciones While - - Wend nos permite crear bucles que se ejecuten
slo si una variable, o expresin toma determinados parmetros.
While - - Wend no posee ninguna expresin que permita salir desde dentro del bucle
en un momento dado, sin antes haberlo completado.
VBA posee una instruccin ms potente, es la instruccin Do - - - Loop.
Su sintaxis posee dos formas distintas de utilizacin
Do [{While | Until} condicin]
[instrucciones]
[Exit Do]
[instrucciones]
Loop
O con esta otra sintaxis:
Do
[instrucciones]
[Exit Do]
[instrucciones]
Loop [{While | Until} condicin]
Veamos la primera forma:
Despus de Do nos permite seleccionar While condicin, Until condicin.
Si ponemos While, despus de Do el bucle se ejecutara mientras la condicin sea cierta.
Si escribimos Until, el bucle se ejecutara hasta que la condicin sea cierta.
Si la condicin no fuese cierta se ejecutara el bucle si hemos puesto While. En cambio no
se ejecutara si hubiramos escrito Until despus de Do.
Por lo tanto podra ocurrir, tanto con While como con Until en funcin del resultado de
Condicin, que no se llegara a ejecutar el bucle ni una sola vez.
Si deseramos que siempre se ejecutara al menos una vez el bucle, deberamos usar
While Until despus de Loop.
Supongamos que queremos escribir una funcin a la que pasndole un nmero entero
positivo, nos indique si ese nmero es no primo.
Supongo que no har falta recordaros que un nmero primo es aqul que slo es divisible
por 1 por s mismo.
Este es el mtodo que voy a emplear. S. Ya se que no es el ptimo:
Dividir el nmero entre valores enteros, empezando por el dos, y a continuacin por
los sucesivos valores impares, hasta que encontremos un valor que divida de forma
exacta al nmero a probar (su resto = 0).
Si el resto de la divisin da cero indica que el nmero es divisible por ese valor, por lo
que el nmero no ser primo y deberemos salir del bucle.
Seguir con el ciclo mientras el valor por el que se va a dividir el nmero no sea mayor
que la raz cuadrada del nmero.
Necesitis saber que en VBA, el operador que devuelve el resto de una divisin es Mod.
Entrega 10 Estructuras de Control II 10 - 5


Comencemos a programar con VBA - Access

Si dividimos 15 entre 3 da de resto 2 15 Mod 3 2
Ya s que este cdigo es manifiestamente mejorable, pero funciona y me viene bien para el
ejemplo con Do Loop.
Funciona si el nmero que probamos es menor igual que 2.147.483.647
Este es el mximo nmero Long positivo. Este nmero tambin es primo.
Public Function EsPrimo( _
ByVal Numero As Long _
) As Boolean
Dim lngValor As Long
Dim dblRaiz As Double

Select Case Numero
Case Is < 1
MsgBox (Numero & " est fuera de rango")
EsPrimo = False
Exit Function
Case 1, 2
EsPrimo = True
Exit Function
Case Else
dblRaiz = Numero ^ 0.5
lngValor = 2
' Comprobamos si Numero es divisible por lngValor
If Numero Mod lngValor = 0 Then
EsPrimo = False
Exit Function
End If
lngValor = 3
EsPrimo = True
Do While lngValor <= dblRaiz
If Numero Mod lngValor = 0 Then
EsPrimo = False
Exit Function
End If
lngValor = lngValor + 2
Loop
End Select
End Function
Nota:
En este cdigo he usado para calcular la raz cuadrada de un nmero, elevar ste a 0,5.
10 - 6

eduardo@olaz.net E Ed du ua ar rd do o O Ol la az z

En VBA hay una funcin que calcula la raz cuadrada directamente: Sqr(Nmero).
Es equivalente a Nmero^0.5
Habiendo escrito la funcin EsPrimo, en un mdulo estndar, vamos a crear un formulario
en el que introduciendo un nmero en un cuadro de texto, tras pulsar un botn, nos diga si
es primo no.
Cerramos el editor de cdigo y creamos un nuevo formulario y lo ponemos en Vista Diseo.
Aadimos al formulario una etiqueta, un cuadro de texto y un botn.
Nombres aplicados a los controles:
Etiqueta lblMensaje
Cuadro de texto txtNumero
Etiqueta del cuadro de texto lblNumero
Botn cmdPrimo

Ajustamos algunas de las propiedades del formulario, por ejemplo para quitar los
separadores de registro, botones, etc.
Ya que va a ser un formulario con muy pocos controles, ponemos los textos algo mayores
que lo normal, e incluso podemos jugar con los colores.
A m me ha quedado as

Abrimos la ventana de propiedades y teniendo seleccionado el formulario, vamos a la pgina
de Eventos.
Hacemos que al abrir el formulario ponga como ttulo del mismo "Test de nmeros
primos", y como texto de la etiqueta lblMensaje, "Introduzca un nmero
entero".
Private Sub Form_Open(Cancel As Integer)
Caption = "Test de nmeros primos"
lblmensaje.Caption = _
"Introduzca un nmero mayor que cero"
End Sub




Entrega 10 Estructuras de Control II 10 - 7


Comencemos a programar con VBA - Access

Al abrir el formulario quedar as:

Para que el formulario tenga este aspecto, he modificado algunas de sus propiedades:
Propiedad Valor
Selectores de registro No
Botones de desplazamiento No
Separadores de registro No
Estilo de los bordes Dilogo
Vamos a hacer ahora que tras introducir un nmero en el cuadro de texto, y presionar el
botn, nos diga en la etiqueta si el nmero es primo.
Volvemos a la hoja de propiedades y seleccionamos Eventos.
Teniendo seleccionado el botn, activamos el evento Al hacer clic, pulsamos en el
botoncito que aparece con los tres puntos y seleccionamos Generador de cdigo, y a
continuacin Aceptar.
Vamos a escribir el cdigo:
Os recuerdo que detrs de la comilla simple lo que se escriba es un comentario
(lneas en verde). Estas lneas VBA las ignora, sirviendo slo como ayuda al usuario.
Tambin os recuerdo que el espacio en blanco seguido de la barra inferior, al final de
una lnea, hace que la lnea siguiente se considere como la misma lnea.
El dividir as las lneas lo hago como ayuda para la composicin de este texto y para
ordenar el cdigo.
Private Sub cmdPrimo_Click()
Dim strNumero As String
Dim lngNumero As Long

' Pasamos a la variable el contenido _
de txtNumero, sin blancos en las esquinas
' Nz(txtNumero, "") devuelve una cadena vaca _
si txtNumero contuviera Null
' Trim (Cadena) quita los "Espacios en blanco" _
de las esquinas de la Cadena
strNumero = Trim(Nz(txtNumero, ""))

10 - 8

eduardo@olaz.net E Ed du ua ar rd do o O Ol la az z

' IsNumeric(strNumero) devuelve True _
si strNumero representa a un nmero
If IsNumeric(strNumero) Then

' La funcin EsPrimo() _
funciona con nmeros long positivos _
entre 1 y 2147483647
If Val(strNumero) > 2147483647# _
Or Val(strNumero) < 1 Then
lblmensaje.Caption = _
"El nmero est fuera de rango"
txtNumero.SetFocus
Exit Sub
End If
lngNumero = Val(strNumero)
' Format(lngNumero, "#,##0") _
devuelve una cadena con separadores de miles
strNumero = Format(lngNumero, "#,##0")
If EsPrimo(lngNumero) Then
lblmensaje.Caption = _
"El nmero " _
& strNumero _
& " es primo"
Else
lblmensaje.Caption = _
"El nmero " _
& strNumero _
& " no es primo"
End If
Else
lblmensaje.Caption = _
"No ha introducido un nmero"
End If
' El mtodo SetFocus _
hace que el control txtNumero tome el foco
txtNumero.SetFocus
End Sub
Tras presionar el botn cmdPrimo se produce el evento clic, por lo que se ejecuta el
procedimiento cmdPrimo_Click()que maneja ese evento
Este procedimiento lo primero que hace es declarar dos variables, strNumero de tipo
string y lngNumero de tipo Long.

Entrega 10 Estructuras de Control II 10 - 9


Comencemos a programar con VBA - Access

A continuacin asigna el contenido del cuadro de texto txtNumero, procesado primero con
la funcin Nz, que devuelve una cadena vaca si tiene el valor Null, y a continuacin le
quita los posibles espacios en blanco de los extremos mediante la funcin Trim.
Seguidamente pasa por la primera estructura de decisin If, controlando si la cadena
strNumero es de tipo numrico.
Si no lo fuera muestra en la etiqueta el mensaje "No ha introducido un nmero".
Si lo fuera, primero comprueba si la expresin numrica de strNumero est entre 1 y
214748364, rango de valores vlidos en el rango de los Long, para la funcin EsPrimo.
Si no fuera as, muestra el mensaje " El nmero est fuera de rango", lleva el
cursor al control txtNumero y sale del procedimiento.
Supongamos que el contenido de strNumero ha logrado pasar todos estos controles.
Mediante la funcin Val(strNumero) asigna el valor a la variable lngNumero.
Como ya no vamos a utilizar la cadena strNumero para ms clculos, para mostrar el
nmero, le asignamos el resultado de la funcin Format(lngNumero, "#,##0").
Con esta utilizacin, la funcin Format devuelve una cadena formada por el nmero con
los separadores de miles.
La funcin Format tiene un amplio abanico de posibilidades en la conversin de
nmeros y fechas a cadenas de texto.
Merece por s misma un tratamiento ms extenso.
Se lo daremos en una prxima entrega.
El siguiente paso es comprobar si el nmero lngNumero es primo, utilizando la funcin
EsPrimo que escribimos anteriormente.
Si lo fuera, escribiramos en la etiqueta "El nmero " seguido del contenido de la
cadena strNumero, y el texto " es primo".
Si no lo fuera, escribiramos lo mismo, pero indicando " no es primo".
Terminado todo esto llevamos el cursor al cuadro de texto txtNumero mediante su
mtodo SetFocus.
Todo muy bien.
El cliente est contento y el programa responde a lo que nos peda, pero
Casi siempre hay un pero
Viendo lo efectivos y rpidos que hemos sido, al cliente se le ocurre que sera muy
interesante poner dos botoncitos que al presionarlos, dado un nmero cualquiera,
nos muestre el nmero primo inmediatamente mayor menor al nmero que hemos
mostrado.
-Tiene que ser fcil, total ya has hecho lo ms importante y ste es un pequeo
detalle adicional, que no te costar prcticamente nada de tiempo y supongo que no
tendrs problemas para hacrmelo sin aumentar el importe presupuestado
A alguno le suena esta conversacin?.
Y adems, aunque ya has terminado lo que te pedan, como hay que aadirle este pequeo
detalle no te pagan hasta que no lo termines
Decido aadir dos botones con unas flechas en su interior.
Al primero, con una flecha hacia arriba lo llamo cmdPrimoSiguiente, y al segundo, con
una flecha hacia abajo, cmdPrimoAnterior.

Este es el diseo que le doy al formulario:
10 - 10

eduardo@olaz.net E Ed du ua ar rd do o O Ol la az z


Los eventos clic de los dos botones los escribo as:
Private Sub cmdPrimoSiguiente_Click()
' La siguiente lnea hace que se ignoren _
los posibles errores en la ejecucin.
On Error Resume Next
Dim strNumero As String
Dim lngNumero As Long
Dim blnPrimo As Boolean
strNumero = Trim(Nz(txtNumero, ""))
If IsNumeric(strNumero) Then
lngNumero = Val(strNumero)
' Si lngNumero est entre 0 y 2147483646
If lngNumero < 2147483647# And lngNumero >= 0 Then

' Mientras blnPrimo no sea Cierto _
Es decir Mientras lngNumero no sea primo.
Do While Not blnPrimo
lngNumero = lngNumero + 1
blnPrimo = EsPrimo(lngNumero)
Loop
txtNumero = CStr(lngNumero)
cmdPrimo_Click
Else
txtNumero = "1"
cmdPrimo_Click
End If
Else
txtNumero = "1"
cmdPrimo_Click
End If
Entrega 10 Estructuras de Control II 10 - 11


Comencemos a programar con VBA - Access

End Sub
En el cdigo anterior podemos ver algunas cosas interesantes.
Lo primero que nos puede llamar la atencin es la sentencia:
On Error Resume Next
Esta es la forma ms bsica de efectuar un control de los errores que se puedan originar
durante la ejecucin de un programa en VBA.
Simplemente se le est indicando a VBA que si se produjera un error en algn punto del
procedimiento lo ignore y vaya a la siguiente sentencia del cdigo.
El ignorar los errores no es una verdadera forma de control.
Aprenderemos en otra entrega diferentes formas de manejar los posibles errores, ya
sean generados por el cdigo, por datos inadecuados de los usuarios, etc.
Ms adelante nos encontramos con una sentencia If que evala una expresin doble
If lngNumero < 2147483647# And lngNumero >= 0 Then
Para que esta expresin sea cierta, lo tienen que ser a l vez las dos expresiones unidas por
And; es decir lngNumero tiene que ser menor que 2147483647 y simultneamente
tiene que ser mayor igual que 0.
Cuando varias expresiones estn unidas por el Operador Lgico And, para que la expresin
total sea cierta, es necesario que lo sean cada una de esas expresiones. Con que haya una
falsa, la expresin total ser falsa.
Por el contrario, cuando varias expresiones estn unidas por el Operador Lgico Or, para
que la expresin total sea cierta, es suficiente con que lo sea una cualquiera de las
expresiones que la forman.
A continuacin nos encontramos con otro Operador, es el operador negacin Not.
Do While Not blnPrimo
Not hace que la expresin lgica que le sigue cambie su valor.
As si blnPrimo contiene el valor True
Not blnPrimo
devolver el valor False.
La expresin equivale a:
Mientras blnPrimo no sea cierto
Que es equivalente a
Mientras blnPrimo sea falso.
Con ello se ejecutar el cdigo contenido entre la lnea de Do y la lnea del Loop.
Cuando lngNumero sea primo, la funcin EsPrimo asignar True a blnPrimo, con lo
que se saldr del bucle, pondr la cadena de texto del nmero txtNumero en el cuadro de
texto y ejecutar el procedimiento cmdPrimo_Click, como si se hubiera presionado en el
botn [cmdPrimo].
Si el valor de lngNumero no hubiera cumplido con el rango de valores, pone un 1 en el
cuadro de texto txtNumero, y ejecuta el procedimiento cmdPrimo_Click.
En el procedimiento que maneja la pulsacin de la tecla [cmdPrimoAnterior] aunque tiene
una estructura semejante, se introducen unos cambios que considero interesante remarcar.
10 - 12

eduardo@olaz.net E Ed du ua ar rd do o O Ol la az z

Private Sub cmdPrimoAnterior_Click()
' Ignorar el error
On Error Resume Next

Dim strNumero As String
Dim lngNumero As Long

strNumero = Trim(Nz(txtNumero, ""))
If IsNumeric(strNumero) Then
lngNumero = Val(strNumero)
If lngNumero < 2147483648# And lngNumero > 1 Then
lngNumero = lngNumero - 1

Do Until EsPrimo(lngNumero)
lngNumero = lngNumero - 1
Loop
txtNumero = CStr(lngNumero)
cmdPrimo_Click
Else
txtNumero = "2147483647"
cmdPrimo_Click
End If
Else
txtNumero = "2147483647"
cmdPrimo_Click
End If
End Sub
En primer lugar utilizamos una estructura del tipo Do Until, en vez de Do While.
Adems, como condicin no utiliza una variable como en el caso anterior, sino que lo
compara directamente con el valor devuelto por la funcin EsPrimo, que devuelve True
False segn sea el caso:
Do Until EsPrimo(lngNumero)
Con esto nos evitamos utilizar una
variable y una sentencia adicional.
Adems el cdigo resulta algo ms claro..
En este caso, si la variable no supera los
filtros, pone el valor "2147483647" en
el cuadro de texto.

Você também pode gostar