Curso REALbasic

147
Lección 1 Presentando REALbasic Objetivo En esta lección aprenderás a crear y ejecutar una aplicación de REALbasic sencilla. También aprenderás parte de lo que ocurre entre bastidores en REALbasic. Recuerda: El proyecto de Enseñanza de REALbasic tiene como objeto enseñarte ciencia de la computación, de modo que no cubriremos las capacidades de REALbasic en detalle. Existen otros muchos lugares en los que puedes obtener dicha información, empezando con la propia documentación de REALbasic. Crear el proyecto 1 Haz doble clic sobre el icono de REALbasic para ejecutar el software. REALbasic abre la ventana principal correspondiente al Entorno de Desarrollo Integrado (IDE): Figura 1. La Ventana de Proyecto. (captura Figura 1) Esta ventana contiene todos los elementos de tu proyecto. La primera pantalla que ves contiene el Editor de Proyecto. El IDE de REALbasic está compuesto por varios editores presentados mediante paneles accesibles desde las correspondientes pestañas. El Editor de Proyecto se abre automáticamente cada vez que creas un nuevo proyecto o abres alguno de los proyectos existentes. Puedes tener abierta más de una de estas ventanas para cada proyecto, además de poder mantener abiertos varios proyectos simultáneamente si así lo deseas. Sin embargo, probablemente realices la mayoría de tu trabajo con REALbasic en sólo una de estas ventanas. Puede trabajar en una ventana dado que puedes abrir varias pestañas (de forma muy similar a como utilizas un navegador web) para ver diferentes partes de tu proyecto. Dado que te has limitado a ejecutar REALbasic sin abrir un proyecto ya existente, REALbasic te ha ayudado creando un nuevo proyecto con algunos elementos de inicio ya listos en él. La pestaña que ves justo ahora es la correspondiente al Editor de Proyecto, desde el que puedes acceder a todas las partes de tu proyecto de REALbasic. 2. Haz doble clic sobre el elemento Window1 en el Editor de Proyecto. Esta acción abre una nueva pestaña Window1 en la que se muestra el Editor de Ventana, listo para que añadas controles sobre ella. Figura 2. El Editor de Ventana (Captura Figura 2) Advierte que aun puedes ver la pestaña correspondiente al Editor de Proyecto bajo la barra de herramientas, y de hecho puedes hacer clic sobre ella y la pestaña correspondiente a Window1 para cambiar entre ambas. Advierte también que la pestaña correspondiente a Window1 tiene un elemento de cierre justo en la parte derecha. Al cerrar una pestaña sólo se cierra el editor; todos los objetos del programa permanecen hasta que los borres en el Editor de Proyecto. Siempre puedes volver a abrir un editor

Transcript of Curso REALbasic

Page 1: Curso REALbasic

Lección 1 Presentando REALbasic

ObjetivoEn esta lección aprenderás a crear y ejecutar una aplicación de REALbasic sencilla. También aprenderás parte de lo que ocurre entre bastidores en REALbasic.

Recuerda: El proyecto de Enseñanza de REALbasic tiene como objeto enseñarte ciencia de la computación, de modo que no cubriremos las capacidades de REALbasic en detalle. Existen otros muchos lugares en los que puedes obtener dicha información, empezando con la propia documentación de REALbasic.

Crear el proyecto

1 Haz doble clic sobre el icono de REALbasic para ejecutar el software.REALbasic abre la ventana principal correspondiente al Entorno de Desarrollo Integrado (IDE):

Figura 1. La Ventana de Proyecto.

(captura Figura 1)

Esta ventana contiene todos los elementos de tu proyecto. La primera pantalla que ves contiene el Editor de Proyecto. El IDE de REALbasic está compuesto por varios editores presentados mediante paneles accesibles desde las correspondientes pestañas.

El Editor de Proyecto se abre automáticamente cada vez que creas un nuevo proyecto o abres alguno de los proyectos existentes. Puedes tener abierta más de una de estas ventanas para cada proyecto, además de poder mantener abiertos varios proyectos simultáneamente si así lo deseas. Sin embargo, probablemente realices la mayoría de tu trabajo con REALbasic en sólo una de estas ventanas.

Puede trabajar en una ventana dado que puedes abrir varias pestañas (de forma muy similar a como utilizas un navegador web) para ver diferentes partes de tu proyecto. Dado que te has limitado a ejecutar REALbasic sin abrir un proyecto ya existente, REALbasic te ha ayudado creando un nuevo proyecto con algunos elementos de inicio ya listos en él. La pestaña que ves justo ahora es la correspondiente al Editor de Proyecto, desde el que puedes acceder a todas las partes de tu proyecto de REALbasic.

2. Haz doble clic sobre el elemento Window1 en el Editor de Proyecto.Esta acción abre una nueva pestaña Window1 en la que se muestra el Editor de Ventana, listo para que añadas controles sobre ella.

Figura 2. El Editor de Ventana

(Captura Figura 2)

Advierte que aun puedes ver la pestaña correspondiente al Editor de Proyecto bajo la barra de herramientas, y de hecho puedes hacer clic sobre ella y la pestaña correspondiente a Window1 para cambiar entre ambas. Advierte también que la pestaña correspondiente a Window1 tiene un elemento de cierre justo en la parte derecha. Al cerrar una pestaña sólo se cierra el editor; todos los objetos del programa permanecen hasta que los borres en el Editor de Proyecto. Siempre puedes volver a abrir un editor

Page 2: Curso REALbasic

buscando el objeto que quieras editar en la pestaña Proyecto y haciendo doble clic sobre él.

En el centro del Editor de Ventana verás una ventana vacía, lista para su edición. A su izquierda verás el listado de Controles, desde el que puedes arrastrar cualquier control que quieras añadir a la ventana, como por ejemplo botones, etiquetas, y cajas de texto en las que el usuario puede introducir información, etc:

Figura 3. El listado de Controles.

(captura Figura 3)

El menú desplegable de la parte superior del listado de Controles te permite alternar entre los controles incluidos de serie en REALbasic, aquellos que hayas añadido mediante Plugins, los que hayas creado tú mismo, y tus Favoritos.

Durante nuestras lecciones utilizaremos sólo algunos de todos los controles disponibles en este listado, pero explicaremos como funciona cada uno de ellos a medida que los vayamos necesitando. Consulta la Guía del Usuariopara obtener más detalles sobre la función de cada uno de estos controles y sobre cómo crear tus propios controles personalizados.

3 Arrastra un control StaticText en la parte superior izquierda de la ventana (probablemente tendrás que desplazarte por el listado de Controles para encontrarlo).

Figura 4. El control StaticText.

(Captura Figura 4)

Una vez que lo hayas soltado, arrástralo un poco más. Advierte como REALbasic muestra guías de alineación y “ajusta” hacia el punto de la derecha para ayudarte a situar los elementos ajustándose a las Guías de Interfaz de Usuario específicas de tu plataforma.

(Ten en cuenta que REALbasic Professional y REAL Studio pueden crear programas para Macintosh, Windows o Linux a partir de un único código fuente. Y no tienes por qué ejecutar REALbasic en cada plataforma para la que quieras crear el programa; si bien es conveniente que lo hagas para labores de testeo. No hablaremos más sobre este aspecto en las siguientes lecciones; conulta la información disponible en la Guía del Usuario para obtener más información.

El panel Propiedades, que está en la parte derecha del Editor de Ventana, te permite gestionar los ajustes para los cntroles que estén seleccionados en la ventana o bien de la propia ventana:

Figura 5. El panel de Propiedades.

(captura Figura 5)

La mayoría de estos ajustes son obvios; no te preocupes por ahora sobre aquellos que no resultan tan evidentes. Haz clic sobre la propiedad Text: y cámbialo para que se corresponda con “Nombre:” (sin las comillas, sólo como ves en la Figura 5). Advierte que

Page 3: Curso REALbasic

puedes hacer clic en un área de la ventana que no contiene controles para gestionar las propiedades correspondientes a la ventana propiamente dicha. Ten en cuenta también que puedes modificar el tamaño de la ventana una vez que la tengas seleccionada.

4 Arrastra un control TextField en la ventana a la derecha del control StaticText:

Figura 6. El control TextField.

5 Cambia su Name: a “UserName” (una palabra, sin espacios, y sin las comillas).

6 Arrastra un control PushButton en la parte inferior derecha de la ventana:

Figura 7. El Control PushButton.

(Captura Figura 7)

A medida que lo arrastres por la esquina inferior derecha de la ventana, aparecerán guías horizontales y verticales.

7 Cambia su propiedad Caption a “Greet Me”.

Probablemente necesitarás hacer que el botón sea un poco más ancho para que encaje el texto de la etiqueta. Ten en cuenta que puedes cambiar la posición y tamaño de los elementos de la ventan utilizando las mismas técnicas que se emplean en un programa de dibujo.

8 Haz doble clic sobre el PushButton.Esta acción te llevará al Editor de Ventana de modo que pasarás del modo de diseño al modo de edición de los objetos. También puedes cambiar entre el editor de Diseño y el Editor de Código haciendo clic sobre el botón Modo de Edición que no esté seleccionando en ese momento. En la Figura 8 está seleccionado el Editor de Código.

Figura 8. Botones del Modo de Edición.

(captura Figura 8)

Los botones del Modo de Edición cambian del modo de edición de plantilla (lado derecho del control seleccionado) al modo de edición de código (lado de la izquierda seleccionado). También puedes cambiar entre los dos haciendo clic sobre el control. En la platforma Windows, el modo seleccionado aparece pulsado en vez de seleccionado.

El Editor de Código te permite ver todo el código que hayas añadido directamente a la ventana o a los controles contenidos en ella (posteriormente veremos que por lo general algunos o incluso la mayoría del código que gobierna el comportamiento de la ventana y los controles pueden residir en otras parte, pero por ahora no te preocupes de esto). El Editor de Código se abrirá para el Evento más adecuado correspondiente al control sobre el que hayas hecho doble clic.

Figura 9. El panel del Editor de Código.

(Captura Figura 9)

Page 4: Curso REALbasic

Advierte que el Editor de Código te permite editar el código del programa para cada control de la ventana,a sí como otros aspectos que pertenecen a la ventana, utilizando para ello el navegador de la parte izquierda del área correspondiente al editor de código. En Windows, puedes expandir los elementos haciendo clic sobre el signo más y, en Macintosh y Linux, puedes hacer clic sobre el triángulo de despliegue. Prueba a hacer clic sobre algunos de los pequeños signos de más o triángulos de despliegue del navegador y comprueba los resultados.

Por ahora, sólo necesitas entender que todos los controles de la ventana pueden contener código de REALbasic para indicarle qué debe hacer cuando ocurra una acción en particular. En el ejemplo mostrado, se indica al botón lo que debe hacer cuando se haga clic sobre él (es decir, cuando ocurra un evento de Action o Acción). Escribe el código tal y como se muestra en la imagen:

MsgBox UserName.Text

A medida que empieces a escribir la palabra MsgBox, REALbasic intentará anticiparse a lo que estás tecleando, y completará el resto de la palabra:

Figura 10. Completación de código.

(captura Figura 10)

Cada vez que REALbasic haga esto puedes pulsar la tecla Tab para aceptar la sguerencia destacada por REALbasic, o bien seleccionar otra opción de las mostradas en el listado en el caso de que exista más de una, para completar el término que se está introduciendo.

Ten en cuenta que sólo hay un espacio en dicha línea, entre MsgBox y UserName.Text (en particular, sólo un punto y sin espacio entre UserName y Text).

Un apunte: El código de programa de REALbasic no distingue entre mayúsculas y minúsculas. Esto significa que puedes utilizar mayúsculas, minúsculas o una combinación de ambas y el código seguirá funcionando.

Y una última cosa: si haces clic sobre el área gris disponible en la zona izquierda de la línea de código, entonces verás que aparece un punto. Esta acción define un punto de parada (breakpoint)en tu código. Hasta que veamos los puntos de parada en la Lección 3, sólo encárgate de hacer clic sobre el punto rojo para que desaparezca de nuevo.

9 Guarda tu proyecto, tal y como harías con cualquier archivo de otro programa.Si ejecutas un programa tras modificarlo y sin guardarlo previamente, REALbasic se encargará de guardar automáticamente una copia oculta, sólo por si algo va mal mientras se está ejecutando el programa (si REALbasic se cuelga, sólo has de ejecutar REALbasic de nuevo y este te ofrecerá la posibilidad de recuperar la copia oculta). Pero todo resulta un poco más sencillo si te acuerdas de guardar los proyectos por ti mismo. Además volveremos a utilizar este proyecto de nuevo, de modo que asegúrate de recordar donde lo has guardado.

10 Haz clic sobre el botón Ejecutar de la barra de herramientas:

(Captura Figura 10)

Page 5: Curso REALbasic

11 REALbasic abrirá una nueva pestaña de Ejecución, en la que te mostrará el progreso a medida que REALbasic compile la aplicación.

También te permitirá interactuar con el programa mientras se ejecute mediante el depurador (debugger) de REALbasic. Veremos el depurador en el Capítulo 3.

Si REALbasic no puede compilar la aplicación te mostrará los errores que encuentre y que han evitado su compilación. En el caso de que REALbasic te muestre cualquier error regresará de nuevo a la pestaña de Ejecución y destacará la línea en la que se ha encontrado el error. Compureba que has tecleado correctamente el código, soluciona el problema e inténtalo de nuevo.

Cuando la aplicación no tiene errores, REALbasic compilará y ejecutará el programa en su propia ventana. Mientras tanto, la pestaña Ejecución del IDE te permitirá monitorizar la ejecución en el depurador.

Figura 12. Tu primera aplicación REALbasic.

(captura Figura 12)

Cuando el programa se jecute, escribe cualquier cosa en el campo de texto y haz clic sobre el botón Greet Me. Te mostrará exáctamente lo que hayas tecleado dentro de un cuadro de diálogo. Haz clic en OK para cerrar el cuadro de diálogo, y selecciona a continuacón Salir en el menú Archivo. Esta acción te llevará de nuevo al entorno de programación de REALbasic (de hecho, el programa y REALbasic son en realidad dos programas que se ejecutan por separado, de modo que puedes cambiar entre los dos en cualquier momento, tal y como harías entre otros dos programas de tu sistema opertivo).

Un programa está formado por líneas de códigoAhora veremos cómo ha funcionado. Selecciona Referencia del Lenguaje en el menú Ayuda. Dicha acción abrirá el sistema de Ayuda de REALbasic, encargado de proporcionar una completa información sobre el lenguaje de programación de REALbasic y su librería de clases (una librería de clases es una colección de herramientas listas para usar. Todos los elmentos del listado Controles, junto con el resto de objetos listados en la Referencia del Lenguaje en la librería de clase de REALbasic).

Figura 13. La ventana de la Referencia del Lenguaje.

(Captura Figura 13)

Un programa de REALbasic está compuesto por una o más líneas de código. Cada línea indica una cosa al ordenador.

Dichas líneas pueden tener formas diferentes, pero lo más común es la mostrada en este programa: consiste de un comando (en este caso, MsgBox) al inicio de la línea, generalmente (pero no siempre) seguido de un espacio y a continuación uno o más partes de información necesaria para que el comando tenga sentido. Dichas piezas de información se denominan los argumentos del comando. En este caso, el comando MsgBox necesita un único argumento, y que proporciona el texto que se ha de mostrar.

Page 6: Curso REALbasic

Es posible indicar que una línea de código tenga efecto (nos referimos a que el código se ejecute o corra) de varias formas diferentes. En este caso, el código ha fomrado un manejador de evento (event handler) para un control, de modo que se ejecutará cuando ocurra un evento (en este caso, cuando se haya hecho clic sobre el botón). Haciendo clic sobre los triángulos de despliegue disponibles en el listado de objetos en el área izquierda del editor de código mostrará los eventos proporcionados para prácticamente cualquier cosa en la que puedas pensar (librerar el botón del ratón sobre el objeto, pulsar el botón del ratón sobre el objeto, el apuntador del ratón se está moviendo sobre el objeto, cualquier elemento soltado sobre el objeto, etc.) Utilizaremos varios de esto eventos en futuros proyectos. Nuevamente, existe multitud de documentación sobre todos los eventos disponibles para cada tipo de control en la Referencia del Lenguaje incluida de serie.

Un programa también consiste de objetos con propiedadesLa cadena (es decir las palabras) que queremos mostrar mediante el comando MsgBox debe obtenerse a partir de una propiedad del objeto de la ventana denomiando UserName (¿recuerdas cuando definimos el nombre del TextField?). En este caso el término propiedad tiene una similitud muy parecida a su significado en el mundo real: es un atributo de algún objeto (en este caso, el texto introducido por el usuario en él). Para obtener el contenido de una propiedad en cualquier momento, debemos utilizar el nombre del objeto, un punto, y a continuación el nombre de la propiedad en cuestión (todo ello sin espacios, tal y como en UserName.Text en nuestro programa). Cuando REALbasic ejecuta un comando y ve un argumento escrito de este modo, comprueba cuál es el valor de la propiedad en ese momento y utiliza entonces dicho valor para completar el comando.

No te preocupes si por el momento te parece algo confuso; en la siguiente lección veremos con más detalle cómo funciona una línea de código como esa.

Entre bastidores: ¿qué es un Compilador?Resulta útil entender qué ocurre cuando haces clic sobre Ejecutar.

Los ordenadores consisten en disposicione de millones y millones de transistores en circuitos muy completos. Dichos circuitos están diseñados de modo que entre las cosas que pueden hacer se incluyen tareas como almacenar y recuperar números de forma muy rápida, así como tratar dichos números no sólo como información sino también como instrucciones simples para comparar otros números, realizar cálculos sobre dichos números, moverlos de un lugar a otro, etc. Es posible indicar al ordenador (computador) que haga cualquier cosa de lo que es capaz utilizando estas simples instrucciones numéricas (la creación de programas de este tipo se denomina escribir código máquina), pero esto resulta tremendamente ineficiente y frustrante. No obstante, los programas escritos de este modo pueden ejecutarse con una velocidad tan rápida como de lo que sea capaz el propio ordenador. Cuando la velocidad es un factor importante, la gente escribirá una pequeña porción del código más complejo de este modo: tal es así en partes de los juegos de ordenador, por ejemplo, y que se escriben de forma bastante habitual en código máquina.

Para que los humanos puedan escribir programas de ordenador de una forma más eficiente se han tomado dos vías principales. Ambas implican el uso de un lenguaje artificial que proporciona un modo más expresivo y amigable pare representar aquello que queremos que realice el ordenador. Representar el programa de este modo proporciona otra ventaja adicional: el programa está menos acoplado al lenguaje máquina de un tipo de ordenador en particular. Esto es lo que permite que puedas coger un programa de

Page 7: Curso REALbasic

REALbasic que funciona en Macitnosh y (utilizando la versión Professional o Studio de REALbasic) compilar ese mismo programa para que funcione sobre Microsoft Windows, con tan sólo realizar algunos cambios mínimos o ningún cambio en absoluto.

La diferencia entre las dos vías para afrontar el proceso reside en lo que el ordenador ha de lleva a cabo para hacer lo que indican estos comandos correspondientes al lenguaje de “alto nivel”:

• Un intérprete se limita a leer el código y hacer aquello que indique el programa, mientras que

• Un compilador lee el código del programa correspondiente al lenguaje de alto nivel y produce un programa en código máquina que hace exáctamente lo mismo.

Cada una de estas opciones está destinada a propósitos distintos. Por varios motivos, los lenguajes de programación interpretados tienden a ser un poco más sencillos de escribir para los humanos, pero los programas ejecutados son considerablemente más lentos en comparación con los programas compilados. Los lenguajes de programación compilados imponen por lo general algunas restricciones más sobre el programador, con el objeto de que el compilador pueda producir un código más rápido. Los ordenadores modernos son tan rápidos que los más sencillos lenguajes de programación interpretados resultan mejores para tareas sencillas dado que facilitan al máximo la tarea de programación.

REALbasic es un compilador, pero el lenguaje de REALbasic es muy amigable, particularmente para un compilador. No obstante, existen algunas restricciones impuestas sobre tí como programador para que el compilador de REALbasic pueda producir programas en código máquina más rápidos. En la práctica dichas restricciones no sólo derivan en programas más rápidos, sino que en los programas complejos, la mayoría de estas restricciones también permiten encontrar errores lógicos.

Una última anotaciónPor último: este curso es un curso sobre ciencias de la computación en las que se utiliza REALbasic. Te enseña sólo lo suficiente sobre REALbasic para aprender ciencias de la computación. Deberías leer más sobre las características de REALbasic en la Guía del Usuario y en el resto de la documentación. En particular, probablemente deberías de leer justo ahora el Capítulo 2 de dicha guía, en la que se describe con más detalle cómo funciona el IDE.

Page 8: Curso REALbasic

Lección 2 Expresiones, Asignaciones y Tipos

ObjetivoEn esta lección aprenderás cómo realizar cálculos simples; cómo mover la información de un punto a otro y un poco sobre los diferentes tipos de datos de REALbasic.

Un Saludo MejoradoAbre el proyecto de la Lección 1. Vamos ha conseguir que sea capaz de hacer un mejor trabajo a la hora de saludar a alguien.

1 Abre Window1, y haz doble clic sobre el botón, de modo que REALbasic muestre de nuevo el manejador del evento Action correspondiente al botón.

Aquí fue donde introdujimos el código en nuestra última lección.

2 Cambia el manejador del evento Action para que sea:

MsgBox “Hello “ + UserName.Text

Ten en cuenta que hay un espacio después de la coma, antes de la doble comilla.

3 Guarda el proyecto de nuevo, y ejecútalo.

Mucho mejor.

Lo que hemos hecho ha sido proporcionar a REALbasic con una Expresión más sofisticada como argumento del comando MsgBox. Esta expresión añade algo de texto delante del texto obtenido mediante el campo UserName.

Sobre las ExpresionesYa has visto expresiones en muchas ocasiones, en la clase de matemáticas. Algo como esto:

3 + 4 x 5

es una expresión. Te indica varias cosas (añadir y multiplicar, en este caso), y proporciona toda la información que necesitas hacer sobre estos elementos (los números que vas a añadir y multiplicar), y una vez que lo hayas hecho obtendrás el resultado. Hallar el resultado de una expresión se denomina evaluar la expresión.

Todo esto también se aplica a las expresiones en los programas de ordenador, exceptuando que en este caso existen muchas más operaciones disponibles en comparación con las que puedes utilizar en matemáticas, y que pueden operar sobre una variedad de elementos mucho mayor y no sólo sobre números (tal y como hemos podido ver en nuestro programa, donde se encuentra una expresión que contiene fragmentos de texto).

De modo que este es el principio general:

En cualquier parte de un programa de ordenador donde se espera que proporciones algún fragmento de información, dicho fragmento puede sustituirse por una expresión.

Page 9: Curso REALbasic

En realidad es un poco más complicado que eso; esta es la versión completa:

En cualquier parte de un programa de ordenador donde se espera que proporciones algún fragmento de información, dicho fragmento puede sustituirse por una expresión, siempre y cuando una vez que se evalúe dicha expresión su resultado sea del tipo correcto.

Sobre los TiposEn un programa de ordenador cada fragmento de información se tiene un tipo. Esto es así porque sólo puedes hacer una serie de cosas sobre ciertos tipos de información. Por ejemplo, no tiene sentido multiplicar dos cadenas de texto.

Lo importante sobre un tipo es las operaciones que puedes realizar sobre algo de dicho tipo.

Por ahora, sólo necesitas saber que cualquier descripción de un comando u operación en la documentación de REALbasic especificará de qué tipos deben ser los argumentos pasados o la operación, y de qué tipo debería ser el resultado. REALbasic te mostrará un mensaje de error en el caso de que intentes ejecutar un programa que rompa estas reglas. (Una operación es algo como + o * en matemáticas. Otro ejemplo es el + en nuestro programa, que e encarga de concatenar dos cadenas de texto).

En nuestro programa la expresión utiliza el símbolo + con el que ya estás familiarizado de las matemáticas, pero en este caso no significa adición (sí significa adición en el caso de que lo utilices con números). Si incluyes cadenas de texto a ambos lados de dicho símbolo, cuando el ordenador evalúe esta expresión formará una nueva cadena en la que añadirá la segunda cadena justo a continuación de la primera (esto se conoce formalmente como concatenar las cadenas). Ten en cuenta que en la expresión de nuestro programa, hemos indicado a REALbasic qué parte de la línea del programa era una cadena encerrándola entre comillas. Si no lo hacemos así, REALbasic intentará interpretar dicha parte de la línea como si fuera un comando y producirá un error cuando intentes ejecutar el programa (veremos qué ocurre cuando hagas esto dentro de un momento).

Es importante entender que los espacios son caracteres tal y como las letras y los números. En nuestro ejemplo, hemos puesto un espacio detrás de la coma, dentro de las dobles comillas, porque si no lo hubiésemos hecho así el ordenador habría sido muy obediente y produciría un resultado como este:

Hola,Chico

En vez de

Hola, Chico

ExperimentoPrueba a experimentar con la expresión. Por ejemplo, puedes crear una sentencia más larga. ¿Cómo harías que el ordenador produjese un resultado como este? (pista: puedes usar el operador + más de una vez dentro de la misma expresión).

Hola, Chico. ¿Qué tal estás?

Page 10: Curso REALbasic

Un ErrorVeamos qué ocurre si introducimos de forma deliberada un error en nuestro programa. Cambia el código para que sea así:

MsgBox “Hola, “ + UserName.Text + 2

Cuando intentes ejecutar esto, REALbasic pondrá un bug a la izquierda de la línea problemática y mostrará un mensaje en la parte inferior de la ventana.

Figura 14. El Editor de Código, mostrando un error del compilador.

(Captura Figura 14)

Un error de tipo es cuando no puedes realizar la operación que has indicado sobre los tipos de elementos proporcionados. En este caso, REALbasic espera que si estás añadiendo un número a algo, entonces dicho elemento debería ser también un número. Y si estás añadiendo una cadena a un elemento, entonces debería ser una cadena. Si quisieras concatenar un 2 a la cadena, entonces deberías de incluirlo entre comillas, así:

MsgBox “Hola, “ + UserName.Text + “2”

Nuevamente, las comillas indican a REALbasic que el elemento interno es una cadena.

Por cierto, ¿has advertido que a medida que has estado trabajando con REALbasic, cada vez que apuntabas cualquier elemento con el apuntador, la línea situada en la parte inferior de la ventana proporcionaba información útil sobre dicho elemento?

Otro ejemploIntentemos un ejemplo más complicado. Crearemos un programa que sumará varios números:

1 Selecciona Nuevo Proyecto en el menú Archivo.

2 Abre el Editor de Ventana correspondiente a Window1, arrastra tres controles TextField, una línea y un PushButton sobre la ventana. Ordénalos y ajusta el texto del botón para que la ventana tenga el siguiente aspecto:

Figura 15. Arrastra tres TextFields, una línea y un PushButton en la Window1.

(Captura Figura 15)

Es buena idea proporcionar nombres identificativos a cado uno de los objetos imporantes del programa. Puedes definir el nombre haciendo clic sobre el objeto para cambiar a continuación el campo Name: en el panel de Propiedades. De arriba hacia abajo, utiliza los siguientes nombres para los TextFields: Source1, Source2 y Result. Cambia el nombre del PushButton a EvaluateButton.

3 Haz doble clic en el PushButton y configura su manejador Action como:

Result.text = Str(CDbl(Source1.text) + CDbl(Source2.text))

Page 11: Curso REALbasic

No te olvides de guardar el proyecto en este momento. Lo utilizaremos de nuevo en la siguiente lección.

Para comprender la expresión necesitas conocer algunas cosas:

• Además de las operaciones y de los comandos que hemos visto hasta aora, REALbasic tiene funciones. Cuando se evalúa, una función proporciona un valor de algún tipo, como por ejemplo una cadena o un número. Las funciones están escritas como un nombre seguido por una serie de argumentos entre paréntesis. CDbl es una función con ún único argumento de cadena, y devuelve dicha cadena convertida en un número. Del mismo modo, Str tiene un único argumento que es un número, y devuelve dicho número como cadena.

• Dado que puedes utilizar una expresión en cualquier parte en la que se espera algún tipo de información, entonces también puedes incluir una expresión dentro de otra expresión. El ordenador evaluará dicha expresión de dentro hacia afuera. Esto puede sonar algo intimidatorio, pero es algo que probablemente ya te resulte familiar. Una expresión numéricoa como 3 * (4 + 5) está formada por la expresión (4 + 5) dentro de una expresión más larga, y esto funciona exactamente del mismo modo en REALbasic, donde puedes utilizar los paréntesis para controlar el orden en el que se evalúan los elementos.

Ahora ya tenemos suficiente información para comprender qué significan las anteriores expresiones. Veámoslas desde dentro hacia afuera:

Expresión(es) Significado

Source1.text y Source2.text El contenido que se haya escrito en los dos campos superiores de Window1.

CDbl(Source1.text) yCDbl(Source2.text)

El valor numérico de lo que se haya escrito en los campos (este será 0 si lo que se haya escrito no puede ser interpretado como número).

CDbl(Source1.text) +CDbl(Source2.text)

Añadir los números de ambos campos.

Str(CDbl(Source1.text) +CDbl(Source2.text))

Convertir el resultado de nuevo en cadena.

Advierte cómo hemos tenido que asegurarnos sobre el tipo de lo que se suministra en cada uno de los pasos: Los TextFields contienen cadenas, de modo que tenemos que convertirlos en número de modo que podamos sumarlos (2 no es lo mismo que “2”, dado que 2 + 2 = 4, mientras que “2” + “2” = “22”). A continuación hemos convertido el resultado de nuevo en una cadena, dado que el comando MsgBox no acepta un número.

Más ErroresIncluso los profesionales cometen errores; aquí puedes ver algunos errores que puedes introducir de forma accidental, el mensaje mostrado por REALbasic cuando intentas ejecutar el programa y la explicación del error:

Page 12: Curso REALbasic

Código Mensaje de Error Explicación

MsgBox Username. Error: Los parámetros no son compatibles con esta función

El argumento del comando, función u operador es del tipo erróneo; en este caso, Username es un TextField, y no puedes mostrar un TextField — tienes que mostrar su propiedad de texto: Username.text

MsgBox “hola,”Username.text

Error de Sintaxis Has olvidado el +

MsgBox “hola,”+ Usernametext

Error: Este método o propiedad no existe

Has olvidado el punto entre Username y text.

MsgBox “Hola, +Username”

No hay error Esto funciona, pero muestra Hola, + Username. Esto muestra la importancia de poner las comillas en el sitio correcto.

ExperimentoPrueba a cambiar el proyecto para que multiplique dos números. El símbolo de la multiplicación en REALbasic es * (de modo que podemos escribir un cuatro como 2 * 2). También puedes hacer la división con el símbolo /. La resta es el símbolo que ya conoces.

Si ya lo has probado, comrpueba si el programa funciona con números con parte decimal o con números negativos.

¿Qué ocurre si conviertes el proyecto para que haga divisiones y la pides que divida entre cero?

Page 13: Curso REALbasic

Lección 3 Condicionales y el Depurador

ObjetivoEn esta lección aprenderás cómo programar REALbasic para tomar decisiones sencillas, y cómo utilizar el depurador de modo que puedas comprobar la ejecución del código en REALbasic línea a línea.

Una ampliación simple sobre el programa anteriorPongámonos directamente en ello.

1 Abre el programa que hemos guardado en el segundo ejemplo de la lección anterior.

2 Como antes, con el Editor del Proyecto delante, abre el Editor de Ventana correspondiente a Window1 y amplía su listado de eventos en el navegador correspondiente al Editor de Código, de modo que podamos acceder a su manejador de evento Action.

3 Cambia el manejador de evento por esto:

If Source1.text = “salir” then Beep QuitElse Result.text = Source1.text + Source2.textEnd if

4 Ejecuta la aplicación. Escribe varias cosas y pulsa el botón, a continuación prueba a escribir salir en el campo superior.

Aquí encontramos un par de comandos nuevos (beep y quit), cuyos efectos deberían ser obvios. Lo interesante aquí es la construcción If-Then-Else-End If que nos proporciona el comportamiento especial cuando hayas introducido la palabra salir. Veremos con más detalle la construcción If-Then-Else-End If a continuación. Por ahora, vamos a utilizar el depurador de REALbasic para observar la ejecución del código.

5 Haz clic en la línea gris situada a la izquierda de la primera línea (el comando if). Advierte que se crea un punto de color rojo en dicha línea.

6 Ejecuta el programa, escribe algo en los campos y haz clic sobre el botón.

REALbasic ejecutará automáticamente el programa pero tan pronto como vaya a ejecutar la línea sobre la que hemos incluido el punto de parada, se encargará de traer el ID de REALbasic a primer plano, cambiar a la pestaña Ejecutar y mostrando dicha línea resaltada para indicarnos que está a punto de ejecutarla:

Figura 16. El depurador de REALbasic.

(Captura Figura 16)

Page 14: Curso REALbasic

También verás el panel Variables (así como un menú desplegable del que no nos ocuparemos por el momento):

Figura 17. El panel de Variables.

(Captura Figura 17)

El panel Variables muestra toda la información que está disponible para dicha parte del programa. Por motivos que abordaremos en una lección posterior, Self se refiere a la ventana, mientras que Me se refiere al objeto para el que se está ejecutando el código (es decir, el PushButton). De modo que si haces clic sobre cualquiera de estos enlaces, REALbasic cambiará el panel de variables para mostrar las propiedades actuales de la ventana o del botón:

Figura 18. El panel de Visor de Objeto para el PushButton.

(Captura Figura 18)

El menú desplegable situado sobre el panel de variables te indica de qué objeto estás viendo las propiedades en ese momento. Si accedes a dicho menú te permitirá regresar para ver las propiedades de las que partiste (dado que las propiedades pueden ser objetos que, a su vez, también contienen propiedades, puedes “navegar” por unas cuantas capas de objetos; el menú te permite regresar hacia el punto original).

Veremos más detenidamente cómo usar dichas características en siguientes lecciones, pero por ahora veremos cómo podemos ir paso a paso a través del código.

7 Haz clic sobre el botón Paso en la barra de herramientas del Depurador.

Ahora aparecerá destacada la siguiente línea que se vaya a ejecutar (la línea dependerá de lo que hayas escrito en los TextFields en el programa en ejecución).

En este programa, no existe ninguna diferencia entre Paso y Paso Dentro. Cualquiera de estos dos comandos avanzará una línea cada vez a lo largo del programa en ejecución. También puedes Detener el programa en ejecución o bien seleccionar Reanudar, en cuyo caso se saldrá del depurador y se continuará la ejecución del programa normalmente (hasta que el programa encuentre nuevamente una línea con un punto de parada asignado). También puedes añadir y eliminar puntos de parada desde el depurador haciendo clic sobre cualquiera de los guiones situados en el margen izquierdo del código. Si una línea de código no tiene un guión a su izquierda entonces no puedes incluir un punto de parada sobre ella.

Puedes cambiar entre REALbasic y el programa en ejecución en cualquier momento, aunque si el programa está detenido en el depurador entonces comprobarás que no es tan ágil cuando cambies de nuevo a él.

Asegúrate de usar el depurador a discrección a lo largo de nuestras lecciones, de modo que puedas ver lo que está ocurriendo en cada momento.

Un + más inteligenteNo hemos terminado aún con el If-Then. Veamos un ejemplo más completo.

Page 15: Curso REALbasic

Dado que el + puede funcionar tanto con cadenas como con números, hagamos que nuestro proyecto se encargue de hacer cada una de estas operaciones automáticamente: si lo que se ha introducido en cada campo es un número los sumaremos, si no concatenaremos las dos cadenas (la concatenación es el nombre de lo que hace el símbolo + sobre dos cadenas).

1 Cambia el manejador del evento a esto:

If (CDbl(Source1.text) = 0 and Source1.text<>”0”) or_ (CDbl(Source2.text) = 0 and Source2.text <>”0”) then Result.text = Source1.text + Source2.textElse Result.text = Str(CDbl(Source1.text) + CDbl(Source2.text))End if

Nuevamente, estamos interesados en la estructura if-then para la toma de decisiones, y que en su forma completa tiene el siguiente aspecto:

If ... Then...Else...End If

Tal y como se indica aquí con los puntos suspensivos (...), existen tres lugares donde puedes introducir expresiones y comandos de REALbasic. Para completar un If-Then en REALbasic:

• En la primera línea, entre el Ify el Then se encuentra una expresión Booleana (profundizaremos sobre esto más adelante);

• Después de dicha línea, puedes tener tantas líneas como desees de comandos REALbasic;

• Si lo deseas, puedes tener una línea con la palabra Else, seguida de otros comandos REALbasic adicionales.

Y todo ello finaliza con End If.

Expresiones BooleanasEn las anteriores lecciones hemos visto las expresiones de cadenay las expresiones numéricas. Las expresiones Booleanas son muy similares, excepto que las expresiones Booleanas sólo contemplan los valores True (verdadero) y False (Falso). True y False son valores que se pueden manipular con REALbasic, tal y como ocurre con los números y las cadenas.

Hay dos tipos de expresiones Booleanas. La lógica Booleana fue inventada por George Boole (1815-1864), y fue presentada por primera vez en An Investigation of the Laws of Thought (1854). Es un sistema muy simple, y cualquier programador de ordenadores debería estudiar sus fundamentos:

Page 16: Curso REALbasic

• Simple: Las expresiones Booleanas comparan algo con otro valor en el programa, generalmente utilizando operaciones como = (igual), <> (no igual), <= (menor o igual que), o bien >= (mayor o igual que). En algunos casos una propiedad o bien algún otro elemento del programa será simplemente un valor Booleano. Las cajas de verificación que ves en la paleta de propiedades cuando haces clic sobre un control suponen un buen ejemplo. Source1.Bold es una expresión Booleana válida, dado que existe una caja de verficación Bold en la paleta Propiedades cuando haces clic sobre uno de los TextFields; de modo que la expresión Source1.Bold se evaluaría a True cuando la caja de verificación correspondiente a Bold está activada; y

• Compuesta: Las expresiones Booleanas están formadas a partir de otras expresiones Booleanas (que a su vez pueden ser simples o compuestas), utilizando los Operadores Booleanos Not (no), And (y) u Or (o).

Tal y como ocurre con los otros tipos de expresiones, necesitarás utilizar con frecuencia paréntesis en las expresiones Booleanas para aclarar en qué orden deben realizarse las operaciones. De igual modo, tal y como ocurre con expresiones de otro tipo, REALbasic halla el valor final desde dentro hacia afuera. Vemos cómo hace REALbasic para evaluar la expresión en nuestro programa:

(CDbl(Source1.text) = 0 and Source1.text <> “0”) Or (CDbl(Source2.text) = 0 and Source2.text <> “0”)

Expresión(es) Significado

Source1.text Los contenidos de Source1.

CDbl(Source1.text) El valor numérico de Source1.text si es un número, o bien 0 de lo contrario.

CDbl(Source1.text) = 0 Cierto (true) si Source1.text se evalúa como 0; falso (false) de lo contrario.

Source1.text <> “0” Cierto (true) si Source1.text es algo distinto de “0” (ten en cuenta que <> significa no es igual a).

(CDbl(Source1.text) = 0 And Source1.Text <> “0”)

Cierto (true) si CDbl(Source1.text) se evalúa como 0, cuando el texto en Source1 no es “0”. En otras palabras, True si el usuario ha introducido algo que no es un número (recuerda: algo que no es un número proporcionado como argumento de CDbl se evalúa como 0).Ten en cuetna que el And entre las dos expresiones se evalúa como True (cierto) si ambas expresiones a ambos lados son ciertas.

(CDbl(Source2.text) = 0and Source2.text <> “0”)

Lo mismo, sólo que en este caso aplicado a lo que se haya introducido en Source2.

Page 17: Curso REALbasic

Expresión(es) Significado

(CDbl(Source1.text) = 0 and Source1.text <> “0”)Or(CDbl(Source2.text) = 0 and Source2.text <> “0”)

Cierto (true) si tanto Source1 o Source2 tienen un valor distinto de un número.

Advierte el uso de los paréntesis para aclarar el orden de las operaciones. Existen reglas sobre el modo en el que REALbasic evalúa las expresiones en el caso de que no se usen los paréntesis:

CDbl(Source1.text) = 0 and Source1.text <> “0” Or CDbl(Source2.text) = 0 and Source2.text <> “0”

Pero es mucho más simple y claro usar los paréntesis.

Puedes encontrar más útil echar un vistazo al modo en el que se evalúa esta expresión usando un tipo de diagrama denominado árbol de sintáxis:

Figura 10. Árbol de sintáxis.

(Captura Figura 19)

REALbasic evalúa la expresión desde la parte inferior del árbol de sintáxis hacia arriba. En la parte inferior de cada línea en el diagrama hay una expresión Booleana que proporciona un resultado Booleano, y en la parte superior de dicha línea se encuentra un operador Booleano que combina estos valores Booleanos, formando una expresión Booleana más extensa.

De modo que ahora puedes ver como funciona toda la construcción if-then-else en nuestro código. Si el usuario no introduce números en ambos campos, entonces REALbasic concatena las cadenas:

Result.text = Source1.text + Source2.text

De lo contrario, el orenador suma los valores introducidos en los campos (la parte que sigue al Else):

Result.text = Str(CDbl(Source1.text) + CDbl(Source2.text))

Por cierto, el otro operador Booleano del que no hemos hablado, Not, es muy sencillo de usar: Cuando pones Not delante de un valor Booleano x, entonces la expresión Not se convierte en True si x es False, y se evalúa como False en el caso de que x sea True. De modo que una expresión equivalente para nuestro manejador de evento podría ser:

If Not((CDbl(Source1.text) = 0 and Source1.text <> “0”) or_(CDbl(Source2.text) = 0 and Source2.text<>”0”)) then Result.text = Str(CDbl(Source1.text) + CDbl(Source2.text))Else Result.text = Source1.text + Source2.text

Page 18: Curso REALbasic

End if

Aquí hemos hecho algunas cosas:

Hemos aplicado el operador Not a toda la expresión esntre el If y el Then. Ten enc uenta que hemos encerrado toda la expresión entre paréntesis, de modo que el Not se aplique a los resultados de toda la expresión en vez de hacerlo sólo sobre la primera parte de ella; y

• Hemos intercambiado los comandos entre el Then y el Else con aquellos entre el Else y el End If.

Por último, necesitas saber que el Else que forma parte de un If-Then es opcional. De modo que podemos sumar sólo los números e ignorar el resto con este código:

If Not((CDbl(Source1.text) = 0 and Source1.text<>”0”) or_(CDbl(Source2.text) = 0 and Source2.text<>”0”)) then Result.text = Str(CDbl(Source1.text) + CDbl(Source2.text))End ifEnd Sub

ElseIfSi necesitas crear una secuencia de decisiones, entonces puedes poner varias instrucciones if-then dentro de otras instrucciones if-then, como aquí:

If (CDbl(Source1.text) = 0 and Source1.text<>”0”) or_ (CDbl(Source2.text) = 0 and Source2.text <> “0”) then Result.text = Source1.text + Source2.textElse If Result.text = “salir” then Quit Else Result.text = Str(CDbl(Source1.text) + CDbl(Source2.text)) End ifEnd ifEnd sub

Esto es perfectamente legítimo y funcionará tal y como puedes esperar. Sin embargo, si necesitas crear una secuencia como esta, ten en cuenta que resultará algo difícil de leer y se indentará excesivamente hacia la derecha.

REALbasic proporciona un modo diferente de decir lo mismo, utilizando para ello el comando ElseIf donde se desplaza el próximo If a la misma línea en la que se encuentra el Else, y donde sólo se requiere un EndIf al final. En otras palabras, el código anterior podría escribirse del siguiente modo:

If (CDbl(Source1.text) = 0 and Source1.text <> “0”) or_ (CDbl(Source2.text) = 0 and Source2.text <> “0”) then Result.text = Source1.text + Source2.textElseif Result.text = “salir” then QuitElse Result.text = Str(CDbl(Source1.text) + CDbl(Source2.text))

Page 19: Curso REALbasic

End ifEnd Sub

REALbasic ejecuta la primera parte If o Elseif cuya expresión Booleana se evalúe como True (cierto) y salta el resto.

ExperimentoAsegúrate de que entiendes cómo crear diferentes tipos de expresiones Booleanas y no te asustes de usar paréntesis para controlar el orden en el que REALbasic debe evaluar dichas expresiones. Un par de cosas que puedes probar son:

• Haz que el programa emita un beep y que muestre un mensaje de error en el caso de que el usuario escriba un número negativo, o bien que concatene lo que haya introducido en el caso contrario:

• Haz que el programa salga en el caso de que el usuario escriba salir en ambos campos, pero suma los valores en el caso contrario.

Page 20: Curso REALbasic

Lección 4 Variables y Bucles

ObjetivoEn esta lección aprenderemos a alamacenar información y repetir acciones.

VariablesPara poder hacer algo una cantidad determinada de veces, necesitamos un modo de realizar un segumiento del contador a medida que lo llevamos a cabo. Este contador será un número completo, o entero en términos matemáticos. El elemento en el que mantenemos esta información se denomina variable. Piensa en ello como en un fragmento de papel sobre el que podemos escribir información. El fragmento de papel tiene un nombre de modo que podemos referirnos a él. Cuando utilizamos el nombre de una variable en una expresión, REALbasic evalúa el nombre para obtener la información contenida en la variable con dicho nombre en ese momento.

Repetir un BeepVamos a crear un nuevo proyecto que repetirá una acción durante una cantidad determinada de veces.

1 Crea un nuevo proyecto y abre Window1 en su Editor de Ventana de modo que podamos crear su interfaz de usuario.

2 Arrastra una etiqueta, un PushButton y un Slider en la ventana, y configúralo de modo que tenga el mismo aspecto que puede verse en la Figura 20:

Figura 20. La interfaz de usuario para el proyecto.

(Captura Figura 20)

3 Haz clic en el Slider en la Window1. En el panel de Propiedades configura la propiedades para el estado inicial del Slider con los siguientes valores:

Propiedad Valor

Minimum 1

Value 0

Maximum 10

4 Haz doble clic sobre el PushButton y configura su handler Action así:

Dim counter as IntegerFor counter = 1 to slider1.Value BeepNext

Si estás haciendo esto sobre Mac OSX entonces sonarán varios beeps al mismo tiempo, de modo que cámbialo para que sea:

Page 21: Curso REALbasic

Dim counter as IntegerFor counter = 1 to slider1.Value MsgBox “Beep”Next

5 Ejecuta el proyecto, haz clic en el botón, mueve el slider y haz clic de nuevo en el botón.

Si lo deseas, consulta la ayuda incorporada para saber ás sobre el control Slider.

DimEn una lección anterior mencionamos que REALbasic es un lenguaje de programación compilado, y que esto ocasionalmente significa que tenemos que hacer una serie de cosas para que el programa resultante funcione más rápido (estas cosas también nos permiten detectar nuestros propios errores de programación).

Una de las cosas que tenemos que declarar a REALbasic es de qué tipo es la información que contendrá una variable. Si intentamos asignar el tipo de información erróneo a dicha variable, entonces REALbasic producirá un error.

Sobre esto existe una excepción: existe un tipo de variable denominado variant y que contiene cualquier tipo de dato que puede almacenar REALbasic. Su función es principalmente la de facilitar el uso de código escrito para Visual Basic de Microsoft. REALbasic ha sido diseñado para que sea similar a Visual Basic, y sorprendentemente algunos proyectos complejos de Visual Basic se pueden convertir a REALbasic realizando sólo unos mínimos cambios.

El otro uso de las variantes es si estamos escribiendo código rápido sin importar lo rápido que se ejecute. Si sólo necesitas automatizar una tarea sencilla sin más, entonces el uso de las variantes simplifica almacenar la información. Las variantes hacen que el código corra más lento, y pueden ocultar errores de programación, pero en los proyectos simples esto no suele representar un problema. En el resto de los casos, es preferible evitar el uso de las variantes como si se tratasae de una plaga.

Si pudiésemos utilizar cualquier tipo de variable para almacenar cualquier tipo de información, entonces cada vez que REALbasic quisiera utilizar un valor que hubiésemos almacenado en una variable tendría que comprobar qué tipo de información contiene esa variable, y eso lo ralentizaría todo.

Declarar el tipo es un contrato entre tú y REALbasic: tu prometes no incluir el tipo de información incorrecto en dicha variable, y REALbasic puede confiar en ello para hacer que el código funcione con mayor velocidad.

El comadno para ello se denomina Dim por razones históricas: REALbasic está basado en un antiguo lenguaje de programación denominado BASIC (abreviatura de Beginnerʼs All-Purposse Symbolic Instruction Code). REALbasic es mucho más avanzado que el antiguo lenguaje BASIC, pero comparte el objetivo de su diseño en hacer que las cosas resulten más sencillas para los profanos. También soporta los principales comandos qué hicieron de BASIC lo que es. De igual modo, la mayoría del antiguo código BASIC funcionará con unas simples modificaciones en REALbasic.

Page 22: Curso REALbasic

Uno de estos viejos comandos era dim, abreviatura de dimension. En dichos lenguajes de programación antuguos, dim estaba reservado para declarar arreglos (veremos los arreglos en lecciones posteriores), para lo cual el nombre del comando tenía sentido. De modo que el nombre es un poco extraño ahora porque ha evolucionado a partir de un sentido menos amplio.

Puedes utilizar Dim en tantas variables de tu código como deses, utilizando para ello diferentes comandos Dim. Puedes declarar una variable en cualquier parte del código que desees, pero ten en cuenta que sólo podrás utilizarla a partir del punto en el que hayas declarado la variable y que si la declaras dentro de un bucle entonces sólo podrás utilizarla dentro de dicho bucle (esto resulta útil, dado que sólo necesitas una variable con un pequeño fragmento de código, puedes hacer que cobre vida automáticamente y que sea eliminada una vez que se haya completado).

For... NextEl otro elemento nuevo en nuestro código es el bucle For... Next y que se encarga de reptir una acción. Un bucle For tiene la siguiente estructura básica:

• Una línea que empieza con For, seguida por un nombre de variable, un signo de igual (=), un valor inicial, la palabra to y un valor final.

• Tantos comandos REALbasic como desees, cada uno de ellos en su propia línea (denominado el cuerpo del bucle); y.

• La palabra Next en su propia línea.

En nuestro ejemplo (For counter = 1 to Slider1.value), la variable se llama Counter, el valor inicial es 1 y el valor final es la propiedad Value del Slider1.

Cuando REALbasic encuentra un bucle For-Next define la variable con el valor inicial, a continuación comprueba de forma repetida si la variable es mayor que el valor final y, hasta que así sea, ejecutará el código del cuerpo del bucle añadiendo 1 al valor de la variable.

Si buscas en la Referencia del Lenguaje incorporada, verás que puedes añadir el comando Step al final de la línea For de modo que le permita añadir a la variable un valor distinto de 1 en cada una de las vueltas de ejecución. También existe la palabra reservada DownTo para contar hacia atrás.

En este caso no necesitamos hacer nada de esto, pero eres libre de usar el valor de la variable dentro del cuerpo del bucle en cuaqluier punto donde necesites un valor numérico. También puedes limitarte a cambiar sólo el valor de la variable, quizá para hacer que el bucle finalice con anterioridad. Veremos en un momento cómo acceder y cambiar el valor de una variable.

Por último, ten en cuenta que la variable debe ser de tipo numérico (integer es el único tipo numérico que hemos visto hasta el momento, y de hecho es la variable de tipo más común para controlar los bucles).

Repetir una CadenaProbemos algo un poco más complejo:

Page 23: Curso REALbasic

1 Cambia Window1 para que tenga el aspecto mostrado en la Figura 21:

Figura 21. Window1 en el segundo proyecto.

(Captura Figura 21)

Recuerda: para cambiar el nombre de un control, haz clic sobre él y edita su nombre en el panel de Propiedades. En este caso, el TextField pequeño tiene el nombre SourceString y el control TextArea de mayor tamaño se llama ResultString. Del mismo modo, activa la casilla de verificación Multiline correspondiente a ResultStringen el panel de Propiedades.

El slider tiene los mismos ajuses que en el ejemplo anterior.

2 Introduce el siguiente código en el manejador de evento Action correspondiente al PushButton:

Dim store as String

For counter as Integer = 1 to slider1.Value Store = Store + SourceString.textNextResultString.text = Store

La instrucción Dim crea una variable de cadena de modo que tengamos un lugar en el que crear una nueva cadena. La variable Counter utilizada en el bucle For está declarada como Integer (entero) dentro de la instrucción For.

Dentro del bucle For se encuentra un único comando,

store = store + SourceString.text

Este es un comando de asignación. Se escribe con el nombre de una variable o propiedad, un signo igual (=) y a continuación la expresión. El resultado de la expresión debe ser del mismo tipo que la variable o propiedad situada a la izquierda del signo igual.

Ten en cuenta que en este caso la variable también forma parte de la expresión. Esto es importante de modo que conviene remarcarlo:

La parte derecha de una asignación se evalúa por completo y, entonces, se asigna su resultado a la variable o propiedad situada a la izquierda.

De modo que en este caso, tomamos la cadena actual de la variable store, ponemos el texto del TextField SourceString tras ella, y volvemos a poner el resultado de nuevo en la variable store.

En otras palabras, añadimos el texto del TextField SourceString al final de store. Y hacemos esta operación tantas veces como indique el valor actual del slider (dado que este es el valor que utilizamos para el bucle For).

Dado que cuando creamos una varibale esta tiene un tipo de valor vacío (las cadenas no tienen carecteres y las numéricas tienen 0 como valor), lo que obtenemos es una

Page 24: Curso REALbasic

determinada cantidad de copias de la cadena que se haya introducido en el TextField SourceString.

Ejecuta el código, de modo que puedas comprobar que esto es lo que de hecho ocurre. No te olvides de escribir algo en el campo superior antes de que hagas clic en el botón.

Un par de mejoras sencillasEste no es realmente el objeto de esta lección, pero resulta tan sencillo de hacer que es difícil resistirse:

1 Copia el código del manejado de evento Action del PushButton e introdúcelo en el manejado de evento ValueChanged del Slider.

2 Ejecuta el programa de nuevo. En esta ocasión no tendrás que utilizar el botón. De hecho puedes volver al editor de ventana, seleccionarlo y pulsar la tecla Borrar para eliminarlo.

3 Ahora dirígete de nuevo a la ventana, haz clic sobre el slider y activa su propiedad LiveScroll, y vuelve a probar de nuevo el programa.

Esto cambia el modo en el que el slider llama a su manejador de evento. Antes, se ejecutaba el manejador de evento cada vez que soltabas el botón del ratón; pero con la propiedad LiveScroll ajustada a True, el manejador de evento se ejecuta a medida que mueves el slider. Es más satisfactorio, aunque si el manejador requiere mucho tiempo para ejecutarse puede resultar algo lento, de modo que debes decidir cuando utilizar esta característica.

ExperimentoHay unas cuantas cosas que puedes hacer a partir de aquí, combinando las cosas que hemos aprendido hasta ahora:

• Crea un programa que haga lo mismo que este último, salvo que escribas la palabra ʻbeepʼ en el TextField SourceString. En ese caso, emitirá un beep tantas veces como indice el slider.

• Haz un programa que tenga un slider y un TextField, y que muestre el valor numérico del slider en el TextField. Ten en cuenta que puedes activar la propiedad ReadOnly de un TextField para impedir que el usuario puede introducir datos en él.

• Crea un programa con un botón y un slider, donde el slider haga que el botón se mueva por la ventana. (Esto es sorprendentemente sencillo: mediante código puedes modificar cualquier cosa que veas en en la paleta Propiedades, asignando para ello un valor a una propiedad del botón usando el mismo nombre que veas en la paleta. Si haces clic en el control PushButton, verás que tiene una propiedad Left. Todo lo que necesitas decir es algo como:

PushButton1.Left = Slider1.Value * 5

• Echa un vistazo al resto de propiedades de los otros controles. Puedes hacer todo tipo de cosas divertidas y sorprendentes cambiándolas.

Page 25: Curso REALbasic

Lección 5 Módulos, Métodos y Recursión

ObjetivoEn esta lección aprenderemos los fundamentes sobre el uso de Módulos, Métodos y de la Recursión. Los Módulos y los Métodos son formas de dividir un proyecto complejo en partes más manejables. Los Métodos también nos permiten reutilizar código de una forma especial. La Recursión es un modo de repetir las acciones dentro de un programa.

Repetir una CadenaVamos a crear un proyecto que repita una cadena una cantidad determinada de veces.

1 Ejecuta REALbasic, en el caso de que no esté ya en funcionamiento. Si ya está funcionando, selecciona Nuevo Proyecto en el menú Archivo.

Ahora deberías de tener un nuevo proyecto.

2 Haz clic sobre el botón Añadir Módulo en el Editor de Proyecto.

Un módulo es un lugar en el que puedes almacenar variables y código de programa que puedes utilizar desde cualquier parte del programa.

3 Cambia el nombre del módulo en el panel de Propiedades a “StringLibrary”.

Siempre que estés poniendo un nombre a algo en REALbasic recuerda que no puedes incluir espacios como parte del nombre.

4 Haz doble clic sobre el módulo para abrirlo en el editor de la ventana y haz clic sobre el botón Añadir Método.

REALbasic mostrará un Editor de Código en el que puedes incluir la información sobre el método.

Figura 22. El Editor de Método.

(captura Figura 22)

Un método debe de tener un Nombre de Método.

También puedes proporcionar una lista de argumentos para el méotdo utilizando para ello el campo Parámetros (parámetros es equivalente a argumentos). En el caso de que haya más de un argumento, debes separar los argumentos mediante comas. La definición de cada argumento usa exactamente la misma forma que utilizarías con un comando Dim. En este caso, el método tiene dos argumentos: el primero es una string llamada s, y el segundo es un integer llamado n.

Por último, puedes vonvertir el método en una función declarando un único Tipo Devuelto. En este caso, el método es una función que devuelve una String.

Como verás dentro de un momento, en REALbasic las funciones son exactamente como funciones en matemáticas: tienen un Dominio (el conjunto de posibles argumentos para la función; en este caso, el conjunto de pares de cadenas y los valores enteros) y un Rango (el conjunto de posibles resultados; en este caso, el conjunto de todas las cadenas).

Page 26: Curso REALbasic

Una vez hayas creado un método o una función en un Módulo como este, puedes llamarlo tal y como llamas cualquiera de los métodos o funciones incorporados de serie como por ejemplo la función CDbl que hemos utilizado en las lecciones anteriores.

5 Nombra la función como RepeatString y proporciónale como parametros s as String y n as Integer. Define el Tipo Devuelto como String.

6 Una vez hayas declarado el nuevo método, introduce el siguiente código:

Dim resultText as StringDim counter as Integer

For counter = 1 to n resultText = resultText + s + EndOfLinenext

Return resultText

Esto es similar al otro código que hemos visto antes, excepto por el comando return, y que se encarga de finalizar el método y devolver un valor a la expresión o función de origen.

La ventana del editor de código debería tener el siguiente aspecto.

Figura 23. La función RepeatString.

(Captura Figura 23)

El tipo de dato que hemos usado como Tipo Devuelto en la declaración debe coincidir con el tipo de dato de la variable ʻresultʼ.

7 Abre la ventana Window1 y añade dos TextFields y un slider como el de la Figura 24.

Figura 24. La ventana de String Repeater.

(Captura Figura 24)

8 Define los nombres de los objetos, de arriba a abjo, como: Source, Repetitions y Result. Configura las otras propiedades como:

Control Propiedades

Source Width = 224, Height = 22

Repetitions Width = 224, Height = 16, Minimum = 0, Value = 0, Maximum = 100

Result Width = 224, Height = 182, Multiline = True (activado)

Page 27: Curso REALbasic

Así como podemos añadir métodos a los módulos, también podemos añadir métodos a los objetos en nuestros programas. En este caso vamos a añadir un método a la ventana.

9 Cambia al Editor de Código de Window1 y haz clic en el botón Añadir Método en la barra de herramientas correspondiente al Editor de Código. Nombra el Método Update y deja vacíos tanto sus parámetros como el Tipo Devuelto.

Advierte que el nuevo Método aparece listado en el grupo Métodos del panel izquierdo en la ventana.

Figura 25. El Editor de Código mostrando el método Update de Window1.

(captura Figura 25)

10 Introduce el siguiente código:

Result.text = RepeatString(Source.text, Repetitions.Value)

Advierte que no hay espacios a ambos lados de los puntos.

El Editor de Código debería tener el mismo aspecto que el mostrado por la Figura 25.

11 Expande el item Controles en el navegador y expande a continuación el elemento Repetitions. Introduce el siguiente código en el manejador de evento ValueChanged correspondiente a Repetitions:

Update

Esto llama al método Update de la ventana cada vez que se cambia el valor del slider. Es decir, cada vez que el usuario mueve el slider hacia la izquierda o hacia la derecha.

12 Ejecuta el programa, escribe algo en el TextField superior y mueve el slider.

13 Sitúa un punto de parada en el manejado de evento ValueChanged, y ejecuta el programa de nuevo.

14 Al hacer clic sobre el botón Paso, sigue la ejecución del programa por el manejador del evento, encargado de llamar a Update y que a su vez llama a RepeatString.

Advierte que cada método regresa de nuevo al método desde el que fue invocado. Es posible que también quieras examinar el menú desplegable situado sobre el código en el depurador, lo que te permite ver en orden los métodos de los que aun no has salido.

Si seleccionas uno de estos métodos, el depurador cambiará para mostrar el código correspondiente a dicho método, la línea que se esté ejecutando en ese caso y también las variables para dicho método.

Probableme encuentres de utilidad el botón Paso Fuera. Dicho botón finaliza la ejecución del método actual y reactiva a continuación el depurador tan pronto como regrese al método desde el que se ha llamado. El comando Saltar también es de utilidad: ejecuta un

Page 28: Curso REALbasic

método sin recorrer su código, y reanuda la depuración en la línea siguiente a la llamada del método.

Cómo ejecuta REALbasic un Método o FunciónEs importante comprender qué ocurre cuando REALbasic ejecuta (también decimos “llama”) un método:

• REALbasic encuentra memoria que no esté en uso para contener los argumentos del método y las variables que hayas declarado con Dim en el interior del método. Las variables que hayan sido declaradas como argumentos del método están reservadas y nombradas como si se hubiesen declarado con Dim dentro del propio método.

• REALbasic copia los valores de los argumentos desde el método origen en las variables de argumento dentro del método.

• REALbasic anota la línea en la que se encuentra el método de origen que tendrá que ejecutar después de que se haya llamado al método.

• REALbasic ejecuta el código que se encuentre dentro del método.

Una vez que se haya completado el método, si dicho método es una función, entonces se copia el resultado de vuelta a la expresión desde la que se hubiese llamado la función. Después de salir del método, y antes de reanudar la ejecución en el método de origen, REALbasic marca como vacío el espacio que había sido reservado para las variables y los argumentos del método que acaba de concluir.

Sobre el Ámbito de las Variables LocalesEs importante comprender que cualquier variable declarada mediante Dim o declaradas como argumentos en un método existen sólo dentro del método en cuestión. Será bastante frecuente que en un método se declare mediante Dim una variable con el nombre, digamos, de contador y que dicho método llame a otro método que también haya declarado una variable con el mismo nombre. Estas dos variables son distintas, y ninguna de ellas podrá acceder a la otra. De modo que si cualquier método modifica su variable contador no tendrá efecto sobre la otra.

Estas variables locales se destruyen cuando finaliza la ejecución del método; entendiendo que por “destruir” nos referimos a cualquier valor que dichas variables puedan contener, y el espacio ocupado por dichas variables es reclamado y disponible de nuevo para ser usado por parte de otra variable distinta.

Dado que las variables locales que puede usar un método se crean y destruyen cada vez que se llama al método, sus valores no se conservan entre cada una de las llamadas que se produzcan sobre dicho método.

Cuando declaras el tipo de dato para contador dentro de la línea For, sólo existe mientras que se esté ejecutando el bucle For. Se borra de la mmeoria después de la última iteracción del bucle y REALbasic ejecute la siguiente línea de código dentro del método.

La parte del programa en la que se puede acceder o resulta visible una variable, método o propiedad se denomina su ámbito. Hasta el ahora hemos utilizado dos niveles de ámbito: el ámbito de las variables declaradas mediante Dim dentro de un método está limitado a dicho método (o bien el bucle dentro de dicho método) y el ámbito Global dentro de los

Page 29: Curso REALbasic

módulos. El ámbito Global, que está disponible sólo en los módulos, hace que los elementos estén disponibles para toda la aplicación. Encontraremos otras reglas sobre el ámbito a medida que aprendamos más sobre las diversas características de REALbasic.

Ten en cuenta que puedes modificar los argumentos de un método dentro de ese método, pero sólo estarás modificando una copia del valor que se ha proporcionado en la llamada al método; una vez que el método finalice el valor pasado permanece inalterado. Puedes utilizar la palabra resevada ByRef precediendo a la declaración del parámetro (como en: ByRef x As String), en cuyo caso estarás operando sobre el valor original, y por tanto modificándolo. Esto resulta especialmente útil para devolver múltiples valores desde un método.

RecursiónHemos visto el bucle For-Next en una lección anterior. Ahora vamos a echar un vistazo a un modo completamente diferente de repetir una acción, denominado recursión.

1 Abre el Editor de Ventana para Window1 y selecciona el control slider. Cambia el valor Maximum para el slider Repetitions a 5. De igual modo, asegúrate de que esté desactivada la caja de verificación correspndiente a LiveScroll.

Esto permitirá que resulte más sencillo el trazado de la ejecución.

2 Crea un nuevo méotdo en el módulo StringLibrary con los mismos argumentos y tipos de devolución que RepeatString. Llámalo RepeatString2.

3 Define el código de RepeatString2 como sigue:

Function RepeatString2 (s As String, n As Integer) As String If n <= 0 then Return “” //sin espacios entre las comillas Else Return s+RepeatString2(s,n-1)+EndOfLine End If

Puede que encuentres este código un tanto desconcertante dado que el método se llama a sí mismo mediante la segunda línea Return.

Un método recurrente es similar a una prueba por inducción matemática:

• Tiene una condición recursiva, que es una instrucción If-then (o similar), para determinar cuál de las siguientes dos cosas ha de hacer;

• Tiene un caso base, que provoca el hecho de que deje de llamarse a sí mismo, y realizar algún paso final; y

• Tiene un caso recursivo que causa:

• La realización de un paso no final,

• Cambiar cualquier cosa que produzca la finalización de la condición recursiva a lo que ejecute el caso base y por último

Page 30: Curso REALbasic

• Llamarse a sí mismo.

En nuestro ejemplo, la condición recursiva es:

n <= 0

(Ten en cuenta que <= significa menor o igial que). El caso base es:

Return “”

Y el caso recursivo es:

Return s+RepeatString2(s, n-1)+EndOfLine

El último paso consiste en devolver una cadena vacía, y el caso no final consiste en devolver una copia de la cadena, concatenada con una duplicación más corta de la cadena.

Advierte que cada vez que el caso recursivo vuelve a llamar al método, reduce en una unidad el valor pasado a la siguiente invocación del método como su n.

Recuerda lo que hemos dicho que ocurre cuando se llama a un método: crea una nueva copia desde cero para todas las variables que esté utilizando. La copia antigua aún está ahí, esperando a finalizar la ejecución. Y cada vez que llamamos de nuevo al método nos aproximamos a la condición que provocará la ejecución del caso base y su finalización. Una vez que alcance la condición base, rebobinará a través de cada uno de los métodos no finalizados, completando la evaluación de las expresión que llamaron al método, añadiendo una copia de la cadena y devolviéndola en la evaluación del método anterior en cada etapa.

Cuando utilizar la recursiónLa recursión resulta un tanto extraña y probablemente poco intuitiva para alguien que no haya topado anteriormente con ella.

Debes conciderar el uso de la recursión cada vez que quieras escribir código que realice un proceso en el que, cada vez que se haya completado un paso del proceso, la parte restante deba pasarse de nuevo por el mismo proceso. Algunos ejemplos:

• Crear n copias de una cadena plantea reunir una copia con n-1 copias adicionales.

• Comer un sandwich implica comer todo el sandwich en el caso de que su tamaño sea mínimo; en caso contrario, tendrás que darle un nuevo mordisco y repetir todo el proceso; y

• Dibujar un árbol, dibujas una rama si dicha rama es realmente corta; de lo contrario, dibujas una rama y dibujas a continuación una serie de ramas más cortas unidas a ella.

Una vez que te acostumbras al uso de código recursivo, supone por lo general el modo más sencillo de solucionar un problema.

Por cierto, advierte que en nuestro método, podríamos haber tenido n = 1 como nuestro caso base (y devolver simplemente la cadena), pero entonces nuestro código no podría

Page 31: Curso REALbasic

haber sido capaz de manejar cero copias de la cadena. Esto podría haber sido correcto, pero si no estás seguro, es mejor escribir tu código de modo que sea lo más general posible.

TrázaloDeberías cambiar el código en Update de modo que se llame Repeat2 en vez de Repeat, y trazarlo la suficiente cantidad de veces como para que entiendas lo que está haciendo el código recursivo. Advierte cómo n se decrementa en uno cada vez que el método se llama a sí mismo, y comprueba cómo se añaden a la ventana de la Pila las llamadas aun no finalizadas del método.

ExperimentoHay muchas cosas que puedes hacer a partir de este punto, combinando las cosas que hemos aprendido hasta ahora:

• Llama a Update desde el evento TextChanged en el TextEdit1, de modo que todo quede actualizado cuando modifiquemos el texto;

• Prueba a camiar el rango del slider para que abarque varias miles. Ten en cuenta que las cosas comenzarán a ralentizarse a este nivel. Deberías de poder ver que RepeatString2 comienza a ralentizarse considerablemente antes en comparación con RepeatString. Esto se así porque la creación destrucción de todas esas variables hace que el programa realice una carga de trabajo adicional para obtener el mismo resultado.

• Prueba a crear una versión diferente de Update que sólo añada o elimine suficientes copias de la cadena para que coincida con el slider, como un modo de lograr que pueda manejar una cantidad mayor de repeticiones de una forma más eficiente.

Por ejemplo: si la cadena resultante tiene una longitud de 2000 caracteres, nuestra cadena fuente tiene una longitud de 2 caracteres, y el usuario mueve el slider a 1001, en vez de generar por entero la cadena de 2002 caracteres, podríamos observar que sólo necesitamos añadir una copia de la cadena fuente al final. Si en vez de esto el usuario ha movido el slider a 999, entonces podemos observar que sólo necesitamos borrar los dos últimos caracteres.

Pista: Para acortar una cadena, utiliza la función Left. Ya sabes como puedes alargarla. También has de observar que si el usuario cambia la cadena fuente, no puedes utilizar este atajo: debes de crear toda la cadena de nuevo, de modo que probablemente tendrás que utilizar dos métodos update diferentes.

• Prueba a añadir un segundo slider (llámalo Repetitions2). Usa la caja Results para mostrar Repetitions1.value copias de la cadena como un “párrafo”, seguida por un par de retornos, luego realiza Repetitions2 copias del párrafo. Sólo tendrías que modificar una linea en update para esto, y estaría involucrada la creación de una expresión que llamase dos veces a RepeatString (de modo que en alguna parte dentro de uno de los argumentos de RepeatString debería de haber otra llamada a RepeatString).

• Si piensas que es demasiado sencillo, prueba a escribir un método RepeatString3 que pueda tomar dos strings (las denominaremos string y delimitador; delimitador es el término informático para indicar un ʻseparadorʼ), y dos números. El método devolverá el primer número de copias de la cadena, seguido del delimitador, y repetirá esto la cantidad de veces indicada por el segundo número.

Page 32: Curso REALbasic

Lección 6 Arreglos y Cadenas

ObjetivoEn esta lección aprenderemos los fundamentos de manipular cadenas y de cómo utilizar los arreglos (arrays). También verás un ejemplo simple de un algoritmo.

Dílo Una VezVamos a crear un programa sencillo donde el usuario escribe algo y entonces aparece mostrado en una ventana modal (MsgBox); pero el programa también se encarga de registrar todo lo que introduce el usuario, y protestará en el caso de que se introduzca algo dos veces.

1 Crea un nuevo proyecto. Añade un StaticText, un TextField y un PushButton a Window1 de modo que tenga el aspecto que se puede ver en la Figura 27.

Figura 27. Cómo configurar la ventana.

(Captura FIgura 27)

2 Ajusta la propiedad Title de la ventana a “Di algo”. Define el Name de TextField a Entry, y define la propiedad Text del StaticText a “Di algo”.

3 Haz clic en el botón Editor de Código (situado bajo la pestaña Proyecto) para cambiar al Editor de Código de Window1. Haz clic en el botón Añadir Propiedad en la barra de herramientas.

REALbasic mostrará el área de declaración para las propiedades en el área de edición de código:

Figura 28. El área de declaración de propiedad.

(Captura Figura 28)

Una Propiedad es una variable que pertenece a un objeto. Se declara del mismo modo que se declara una variable mediante el comando Dim. Existe automáticamente siempre y cuando también exista el objeto. También puedes definir el valor por omisión que quieres que tenga la propiedad una vez que el objeto cobre vida (después del =). para definir el ámbito de la propiedad, haz clic en los tres botones del margen derecho en la declaración de la propiedad.

De izquierda a derecha, son Público (que puede ser accedido desde cualquier parte del programa), Protegido (sólo puede ser accedido desde el código en la ventana en la que se encuentra y en cualquier subclase de dicha ventana —veremos las subclases en una lección posterior) y Privado (sólo puede ser accedido por el código de esta ventana). En una posterior lección veremos por qué podrías desear limitar el ámbito de la variable de este modo. Por ahora, dejemos el ámbito como Público.

4 Introduce “Said()” como el nombre de la propiedad y define su tipo de dato a “String”. Deja vacío el último campo.

El nombre de la propiedad y su tipo de dato son obligatorios pero el valor por omisión es opcional. Ten en cuenta que no hay espacios entre Said y el paréntesis de apertura.

Page 33: Curso REALbasic

Sobre los ArreglosLo que hemos acabamos de hacer es declarar un arreglo (array). Un arreglo es una lista numerada de datos. Cada elemento de la lista debe ser del mismo tipo (en este caso, una cadena).

El primer elemento en el arreglo siempre se corresponde con el número cero y cuando estás declarando el arreglo, debes indicar cuál será el número correspondiente al máximo elemento. Tu código entonces puede pedir el valor correspondiente al elemento cuyo índice es n, o bien puede definir dicho valor, siempre y cuando el número utilizado como índice se encuentre en los límites del arreglo.

Puedes declarar la propiedad como:

Said(10) As String

Y entonces tendrás un arreglo de 11 elementos (empezando por 0 y subiendo hasta 10). Si ponemos:

Said(0) As String

Tendríamos un array con 1 elemento. De modo que la forma en la que indicamos a REALbasic un arreglo vacío consiste en no indicar cuál es el índice superior.

Por cierto, acostúmbrate a contar desde 0. Ocurre una gran cantidad de veces en la programación de ordenadores.

Añadir una Función a la Ventana1 Haz clic en el botón Añadir Método de la barra de herramientas. Define el nombre del nuevo método como “AlreadySaid”, declara un parámetro como “s as String” y define el Tipo Devuelto a “Boolean”.

El IDE deber tener el siguiente aspecto.

Figura 29. La declaración de AlreadySaid.

(Captura Figura 29)

No olvides que AlreadySaid es una única palabra.

2 Introduce el siguiente código en el Editor de Código.

//Pre: Nada//Post: s es un elemento de Said, no se cambia ningún otro elemento de Said// y si said ya fuese un elemento, se devuelve true, de lo contrario false

Dim counter as Integer

//devuelve true si s es un elemento de SaidFor counter = 0 to UBound(Said) If Said(counter) = s then Return true

Page 34: Curso REALbasic

end ifNext

Said.append sReturn False

El Editor de Código debería tener ahora este aspecto:

Figura 30. El método de AlreadySaid.

(captura Figura 30)

ComentariosEs útil incluir comentarios para la gente que vaya a leer el código (incluyéndote a tí también). Debes procurar incluir la suficiente cantidad de comentarios para aclarar el cometido del método a quien esté leyendo dicho código por primera vez.

Un comentario se escribe incluyendo dos barras // en una línea. Puedes poner cualquier cosa que quieras detrás de las barras, y REALbasic lo ignorará. Puedes incluir comentarios en dos líneas o bien puedes añadirlos al final de las líneas de código ejecutable. Si haces esto último, entonces se ejecutará el código situado a la izquierda de las barras, mientras que se tratará como comentarios todo aquello que se ponga detrás de las barras.

También puedes poner varias líneas en fila (cada una de ellas precedida de //) como desees. Aunque puedes escribir líneas REALbasic muy largas, es probable que quieras seguir el ejemplo mostrado un poco más arriba, y partir dichas frases o párrafos de comentarios en mútiples líneas de comentarios.

Existen otros do modos de escribir comentarios, pero deberías evitarlos:

• Puedes escribir la palabra REM en vez de las dos barras; esto es una reminiscencia del antiguo BASIC, y no destaca tanto sobre el código como el uso de las dos barras.

• Puedes usar un apóstrofe ʻ en vez de las dos barras, pero deberías de evitarlo, dado que se utiliza en depuración (aprenderemos más de los comentarios con apóstrofe en una lección posterior).

Pre- y Post-CondicionesSi estás afrontando un diseño completo, deberías de escribir Pre- y Post-condiciones para tus métodos, en el caso de que dichas condiciones no sean muy obvias.

Una pre-condición es algo que deba ser cierto para que el código del método funcione correctamente. Con frecuencia diseñarás los métodos para que sean llamados en un punto concreto de la ejecución del programa, y deberías describir en qué estado del programa debería ser mediante un comentario de precondición.

Una post-condición es algo que está garantizado como cierto una vez que se haya completado la ejecución del método.

Page 35: Curso REALbasic

En principio, deberías de poder probar que se cumplan todas las precondiciones de cada método en tu programa, inmediatamente antes de que se llame a cada método. Y las postcondiciones te ayudarán a hacer eso.

Esto puede parece un tanto exigente, pero pronto encontrarás que la escritura de programas complejos y de gran tamño sin bugs resulta un tanto difícil. Si planteas comprobar que se cumplen las precondiciones y postcondiciones de cada método, en cada uno de los pasos realizados, entonces etarás contribuyendo a que tus programas funcionen correctamente. También puedes comprobar las condiciones por ti mismo cuando estés depurando el programa.

En los ejemplos anteriores, no tenemos precondiciones, de modo que sería legal ejecutar el código en cualquier ocasión. La post-condición indica a quien consulte el código precisamente lo que hace.

Las instrucciones Return Actúan como Puntos de SalidaTen en cuenta que podemos Devolver (Return) un valor en el medio de un bucle For que esté en ejecución. Esto terminaría de inmediato el bucle, y por tanto también el método. Esto significa que si llegamo al final del bucle For, sabemos que no hemos encontrado lo que estuviésemos buscando, de modo que Devolvemos (Return) falso.

Accediendo al ArregloTen en cuenta cómo accedemos a un elemento concreto del arreglo: ponemos una expresión numérica en paréntesis después del nombre del array: Said(counter). Esto forma una expresión más larga cuyo valor es ahora, esencialmente, una variable de cadena. Es una variable de cadena, no sólo una cadena, dado que puedes asignar un valor a ello así como obtener su valor, tal y como puedes hacer con cualquier otra variable.

En el ejemplo anterior no asignamos directamente a un elemento del arreglo, pero podemos decir cosas como:

Said(Counter) = “”

Si es que queremos hacerlo.

Existen otra cantidad de modos para añadir elementos o bien eliminar elementos de un array. En este caso, utilizamos el método Append del arreglo para añadir un elemento al final del arreglo. Echa un vistazo a la ayuda en línea para consultar otros modos de añadir y eliminar elementos de un arreglo.

Por último, puedes hallar el elemento superior de un arreglo mediante la función Ubound, tal y como hacemos en nuestro bucle For. Devuelve el valor más alto del índice al que puedes acceder legalmente. Esto significa que:

MsgBox Said(Ubound(Said))

Sería perfectamente válido (asumiendo que existe un mínimo de un elemento en el arreglo), pero:

MsgBox Said(Ubound(Said) + 1)

Page 36: Curso REALbasic

Causará siempre un error.

Ten en cuenta que dado que el primer elemento de un arreglo tiene un índice de 0, la cantidad de elementos en los arreglos es su UBound + 1.

Completa el programa

• Añade el siguiente código al manejador del evento Action para el PushButton en la ventana:

//Pre: Ninguno //Post: alerta al usuario si la entrada ya se ha realizado con anterioridad, almacena la entrada de modo que podamos indicarlo

if AlreadySaid (Entry.text) then MsgBox "Ya has dicho eso." end if

//Limpia la entdada después de que se haya aceptado.

Entry.text = ""

Ejecuta el ProgramaEjecuta ahora el programa, y observa lo que haces cuando escribes algo dos veces. Prueba a usar el depurador para observar cómo funciona el programa (no te olvides de entrar en la línea que llama a AlreadySaid). Del mismo modo, una vez que hayas ejecutado AlreadySaid un par de veces, echa un vistazo al panel Variables en el depurador, haz clic en Self (para ver las cosas en la ventana en vez del método que estás ejecutando), y haz clic a continuación en el enlace subrayado próximo a la variable Said para que puedas ver los contenidos del arreglo.

Debes pasar por esto porque el depurador te muestra dónde residen las propiedades y las variables. Esto significa que debes acceder a las propiedades de la ventana desde el panel de variables de la ventana. Pero el panel de variables que ves inicialmente cuando se activa el depurador se corresponde con las variables definidas para el método en curso.

Más sobre los arreglosEn este programa estamos tratando un arreglo como un conjunto (set) matemático: es una caja de cosas, y sólo queremos saber si una cosa está o no, así como ser capaces de añadir cosas. El Ínidice (el número que proporcionamos para indicar qué elemento queremos del arreglo) es sólo un modo de buscar en orden a través de los elementos.

Intenta pensar como se puede un arreglo de otras formas. Por ejemplo, puede utilizarse un arreglo como una función para un pequeño rango de enteros de algún valor. Esto se ejecutaría con mucha rapidez. Muchos programas precalculan el valor de alguna función complicada y almacena los valores en el arreglo, evitando de este modo la necesidad de tener que realizar cálculos que requieran una elevada cantidad de tiempo.

ExperimentoHay varias cosas que puedes hacer a partir de este punto:

Page 37: Curso REALbasic

• Modificar el programa para que sólo recuerde, digamos, los últimos 5 elementos introducidos;

• Dejar que el programa informe sobre la posición que ocupaba ya un elemento recién introducido.

• Dejar que el programa almacene y compruebe sólo la primera palabra introducida.

• Escribir un programa utilizando un arreglo para que acepte una serie de números informando a continuación de su suma y media. Si tus matemáticas te lo permiten, haz lo mismo sin usar un arreglo, calculando la suma y media a medida que introduzcas los números.

Page 38: Curso REALbasic

Lección 7 Algoritmos

Objetivo

En esta lección comenzaremos a desarrollar y escribir algoritmos.

¿Qué es un algoritmo?El algoritmo es uno de los inventos más importantes en ciencias de la computación. He aquí una definición formal para un algoritmo:

Un algoritmo es un procedimiento no ambiguo para la realización de una tarea determinada en una cantidad de pasos finitos.

Esta definición está adaptada de The Art of Computer Programming de Donald Knuth. Esto significa que un algoritmo debe ser:

• Finito: Un algoritmo siemrpe debe finalizar después de una cantidad finita de pasos (aunque dicho número puede ser arbitrariamente elevado, o incluso desconocido);

• Definido: Cada paso de un algoritmo debe ser concreto (no ambiguo); y

• Efectivo: Cada paso del algoritmo debe ser posible dentro de un periodo de tiempo finito.

Algoritmos y Ciencia de la Computación

Los ordenadores se crearon inicialmente para realizar cálculos que produjesen cosas como tablas de navegación. Posteriormente, se emplearon para almacenar y producir cálculos a partir de datos empresariales. En ambos casos, desarrollar y codificar algoritmos era todo en lo que consistía la programación de ordenadores.

Este no es ya el caso. Varias de las tareas entregadas a los ordenadores actuales consisten en la toma decisiones, el control de un proceso contínuo, o la ejecución de una simulación, frente al completado de cálculos finitos. Probablemente tu vehículo cuente con varios ordenadores, por ejemplo; y piensa en la cantidad de ordenadores utilizados para jugar a juegos. Mientras que el desarrollo y empleo de buenos algoritmos es importante para el desarrollo de este tipo de programas, en la actualidad la programación de ordenadores contempla algo más que simplemente el desarrollo de algoritmos.

No obstante, es tradicional en la ciencia de la computación el estudio de los algoritmos en su fase inicial y con cierto detalle, de modo que haremos lo mismo. La naturaleza finita de los algoritmos (tanto si funcionan como si no; los ejecutas y finalizan en una cantidad de tiempo determinada) significa que constituyen buenos ejercicios de programación autocontenidos. Su naturaleza finita también hacen que sean muy buenos para examinar problemas de eficiencia (lo que veremos en más detalle en las lecciones finales.)

Igualmente, tambié suele dar resultado pensar en los programas de simulación o de control como aplicaciones repetidas de uno o más algoritmos.

Ejemplo de algoritmo: Contar palabras

Page 39: Curso REALbasic

1 Inicia un nuevo proyecto vacío, abre Window1, y diséñala de este modo:

Figura 31. La Ventana de Word Counter.

(Captura Figura 31)

De arriba a abajo, los objetos están titulados Source, CountButton y WordList. Si quieres mejorarlo y hacer que la ventana sea remidensionable, un buen ajuste para la ventana sería de 260 x 300 píxeles. El objeto inferior es una ListBox. Deberías leer sobre cómo utilizar una ListBox en la documentación de REALbasic, aunque veremos las características particulares de este programa a medida que las vayamos necesitando.

Define la propiedad Title de la Window1 a “Word Counter”.

Los ajustes correspondientes del ListBox son:

Propiedad Ajuste

Nombre WordList

ColumnCount 2

HasHeading True

InitialValue Count Word

La propiedad InitialValue tiene un tabulador y no un espacio entre los títulos para cada una de las dos columnas. El mejor modo de introducir los títulos para las columnas consiste en abrir el diálogo Editar Valor haciendo clic sobre elbotón que contiene los puntos suspensivos (los tres puntos) en el área del valor.

Figura 32. El cuadro de diálogo de Editar Valor.

(Captura Figura 32)

Deja sin cambios el resto de los valores para el ListBox.

Esto es lo que hará el programa: el usuario escribe o pega el texto en el campo superior, hace clic en Count y el listado inferior muestra las palabras del texto, junto con el número de veces que se repite cada una de ellas.

Ahora tenemos que desarrollar un algoritmo que se encargue de contar las palabras. Ya hemos aprendido sobre todas las técnicas de programación que necesitaremos para implementar este algoritmo; lo que tenermos que hacer ahora es considerar como debemos combinarlas.

Empezar con un Diagrama de FlujoDibujar un diagrama de flujo puede suponer un modo eficaz de empezar a desarrollar un algoritmo. Un diagrama de flujo es una representación pictórica de los pasos involucrados en la realización de un proceso. Los componentes que utilizas para dibujar una gráfica de flujo son los mostrados en la Figura 33:

Page 40: Curso REALbasic

Figura 33. Los elementos de una gráfica de flujo.

(Captura Figura 33)

Un diagrama de flujo se crea mediante la unión o enlace de diferentes tipos de cajas. Debería tener una única terminal de inicio, y una o más Terminales de salida. Debes escribir texto conciso en las cajas encargado de describir qué es lo que se lleva a cabo en cada etapa, tal y como se muestra en la Figura 34.

Figura 34. Gráfica de flujo para la solución de un problema.

(Captura Figura 34)

El diagrama de flujo describe un conjunto de pasos de toma de decisiones y de cómputo, empezando en la Terminal inicial, y siguiendo las flechas y cajas, realizando las acciones y tomando las decisiones de las cajas hasta que se llega al Terminal final.

Pseudocódigo

Si bien el lenguaje de programación REALbasic es de muy alto nivel y eficiente para escribir código, todos los lenguajes de computador aun son un tanto quisquillosos sobre cada pequeño detalle de lo que precisamos cuando estamos pensando en un algoritmo. Lo que resulta importante cuando comienzas a desarrollar un algoritmo es darte cuenta de la toma de decisiones correcta. Puedes dejar para más tarde el completado de los aspectos específicos, como las declaraciones de variables y los comandos particulares.

Por lo general resulta efectivo describir un algoritmo en algún tipo de lenguaje natural menos formal y estructurado. Esto debe resultar lo suficientemente detallado como para que una persona pueda entender los detalles sobre el funcionamiento del algoritmo, y para que se sienta seguro de que puede programar el algoritmo utilizando un lenguaje de programación real, pero sin entrar en más detalles que esos. No existen reglas fijas sobre cómo llevar a cabo esta tarea, pero a continuación te mostramos un ejemplo sencillo de pseudo-código:

Figura 35. Pseudo-código para la resolución de un problema.

(captura Figura 35)

Esto se lee empezando por la parte superior y realizando cada uno de los pasos cada vez, salvo que en alguna parte se indique dirigirse a algún otro punto.

Algoritmo para Contar PalabrasEchemos ahora un vistazo a lo que tenemos que hacer para contar el número de instancias de cada palabra en la cadena:

• Necesitamos encontrar cada palabra de la cadena. Esto requiere que decidamos qué define una palabra (por ejemplo, ¿la puntuación forma parte de la palabra?);

• Tenemos que almacenar cada palabra que encontremos en una estructura de datos, junto con un contador del número de veces que hemos encontrado dicha palabra; y

Page 41: Curso REALbasic

• Cada vez que encontremos una palabra, necesitamos ver si ya está en la estructura de datos. Si ya está en la estructura de datos, entonces necesitamos incrementar (añadir uno a) su contador; si aun no está en la estructura de datos, entonces necesitamos añadirla a la estructura con un contador de 1.

Durante el desarrollo de un algoritmo para implementar esto, tendremos cuatro partes principales:

• IsWordCharacter: una función que indica si un caracter en concreto es un caracter de palabra o no (en el caso de que no lo sea, lo consideraremos como un caracter de separación, y lo pasaremos por alto);

• WordPosition: Una función que nos indica si una palabra ya ha sido almacenada en nuestra lista de palabras y, en tal caso, dónde se encuentra almacenada dentro de dicha lista;

• AddWord: Un método para añadir una palabra al listado en el caso de que aun no lo esté, y de incrementar su contador en el caso de que ya estuviese guardada; y

• CountWords: Un método para buscar en la cadena, enviando cada una de las palabras al método encargado de su almacenaje, y determinando donde empieza y termina cada una de las palabras llamando para ello a la función que se encarga de definir si se trata del caracter de una palabra.

Este no es el único modo de analizar este problema; y de hecho sería posible escribir todo como un único método, por ejemplo. Sin embargo, este modo ofrece una separación natural de las partes que componen esta tarea.

Registraremos el contador en un par de arreglos relacionados: la palabra en un índice en concreto del primer arreglo tendrá su contador almacenado en la misma posición índice del segundo arreglo. Gestionar esta estructura de datos es una extensión del tipo de código que hemos estado escribiendo en las lecciones anteriores.

El algoritmo para separar las palabras de la cadena es más interesante. Si intentas escribir un método para que se encargue de esto, encontrarás que es un poco más difícil de lo que esperabas.

Tendrás que escribir un bucle que mire las letras de una en una, pero al hacerlo, debe decidir correctamente dónde empieza y termina cada una de las palabras, y debe pasar al método que las almacena sólo los caracteres que formen parte de la palabra.

La estrategia que utilizaremos consiste en iniciar el bucle cada vez mediante uno de los dos estados: InWord o no. (De modo que InWord será una variable de tipo Boolean). Cada vez que iteremos el bucle, comprobaremos si el siguiente caracter es un caracter de límite para el estado acfual (de modo que si es InWord, comprobaremos un caracter que no se corresponde a la palabra, y si no estamos InWord entonces se trata de un caracter de palabra).

Cuando cambiemos el estado no InWord al estado InWord, habremos encontrado el inicio de una palabra, mientras que lo opuesto significará que hemos alcanzado el final de la misma. El cambio de estado también marca el final de una palabra en el caso de que

Page 42: Curso REALbasic

estuviésemos inicialmente en InWord. Empezamos todo el proceso observando el pimer caracter para decidir con qué estado comenzaremos.

Cómo escribir un algoritmo

Cuando escribas un algoritmo siempre has de pensar en lo siguiente:

• La precondición para el algoritmo, que significa lo que debe configurarse antes de que se pueda ejecutar el algoritmo. En nuestro caso, necesitamos una cadena, y necesitamos métodos que puedan almacenar las palabras que encontremos en dicha cadena.

• La postcondición del algoritmo, lo que significa que los cambios en el estado del programa fuera del método propiamente dicho han tenido lugar una vez que ha concluido el algoritmo. Siempre debes ser capaz de especificar esto con total claridad. En este caso, el único cambio realizado después de que se ejecute el programa es que las palabras han pasado por el método de almacenaje. Pero el método puede haber realizado otros cambios al estado general del programa, de modo que necesitamos advertir dichos cambios en el caso de que se hayan producido.

Ten en cuenta que el algoritmo cambia los valores de la variable Counter, pero dado que se trata de una variable local, esta desaparece una vez que concluye el método, y por tanto no debemos de preocuparnos sobre esto.

• Las condiciones que debemos mantener como ciertas a medida que se ejecuta el algoritmo; puede que haya varias etapas durante las cuales se lleven a cabo diferentes acciones. En este caso, estamos incremantando el contador, y pasando una palabra a AddWord cada vez que el contador apunte al final de una palabra.

Los algoritmos también pueden utilizar sub-algoritmos, lo que resuelve una pequeña parte del problema general. En nuestro caso tenemos un sub-algoritmo, contenido en el bucle For en CountWords. También puede ser considerado en los términos descritos arriba:

Precondiciones para el bucle:

• La cadena no debe estar vacía;

• Counter = 1;

• InWord es True si el primer caracter es un caracter de palabra, y False en el caso contrario.

• No hemos emitido la última palabra de la cadena si la cadena finaliza con un caracter de palabra.

La invariante del bucle que es lo que mantenemo como cierto al final del bucle, es similar al de todo el algoritmo, excepto que no envía la última palabra al método AddWord en el caso de que la cadena finalice con un caracter que no sea de palabra.

Figura 36. Nuestro algoritmo expresado como un diagrama de flujo.

(Imagen Figura 36)

Page 43: Curso REALbasic

Ejercicio: Convierte el diagrama de flujo en pseudo-códigoPrueba a convertir la Figura 36 en pseudo-código, para ver hasta qué punto has asimilado lo tratado.

El código realAquí está el método real CountWords, y que debes añadir a tu ventana:

//Pre: Ninguna //Post: El arreglo Word contiene todas las palabras de Source; //El arreglo Count contiene el contador correspondiente a las palabras en Source

Dim InWord As Boolean Dim counter, WordStart As Integer

if Len(Source.text)>0 then //si source no está vacío InWord = IsWordCharacter (1)//invariante Inword (abajo) If InWord then//Invariante Wordstart invariant (abajo) WordStart = 1 end if

for counter = 1 to Len (Source.text)

//Invariantes: //Inword: InWord es cierto si el caracter contador de Source.text ha sido IsWordCharacter //WordStart: WordStart es el último caracter contador dado que el caracter anterior a él no ha sido IsWordCharacter

if InWord then if not IsWordCharacter (counter) then AddWord (Mid (Source.text, WordStart, counter-WordStart)) InWord = false //InWord invariant end if //Invariante WordStart: InWord implica que no necesitamos cambiar WordStart else //no está en InWord

If IsWordCharacter (counter) then InWord = true //invariante InWord WordStart = counter //invariante Wordstart end if end if next end if //El bucle no detecta el final de la cadena como un límiteIf InWord Then AddWord(Mid(Source.text, WordStart, counter-WordStart)) End If

Este código está escrito con una descripción muy formal de las precondiciones, las postcondiciones y las invariantes del bucle en los comentarios. De hecho hemos

Page 44: Curso REALbasic

nombrado las invariantes del bucle y hemos intentado probar donde resultan ciertas dentro del bucle.

Hacer esto resulta un poco excesivo para un ejercicio tan sencillo, pero deberías realizar algo así (o en último término, intentar pensar así) para cualquier algoritmo complejo que vayas a realizar. Lo más próximo que te encuentres a probar que tu código es correcto antes de que lo ejecutes, menos bugs introducirás en tu código.

2 Añade las siguientes propiedades a la ventana: Count() As Integer y Word() as String.

3 Añade la siguiente función a la ventana:

Function IsWordCharacter (n As Integer) As Boolean //Pre: Ninguna //Post: Si el caracter se encuentra en la posición n en Source es una letra o número, Devuelve True //de lo contrario, devuelve false

Dim c As String c = Mid (Source.text, n, 1) Return (c>="0" and c<="9") or (c>="A" and c<="Z") or (c>="a" and c<="z")

El Editor de Código para la función IsWordCharacter debería tener este aspecto:

Figura 37. El método IsWordCharacter.

(Captura Figura 37)

Dividir algo así en sus propios métodos es una buena idea. Podemos cambiar con facilidad lo que define una palabra cambiando sólo un método (quizá haciendo que dependa de un ajustes de las preferencias). También hace que el método encargado de contar las palabras sea más pequeño y sencillo, dado que no necesita incluir este código. En un proyecto más complejo, también puedes reutilizar este método en otro código de procesamiento de cadenas.

4 Añade la siguiente función a la ventana:

Function WordPosition (w As String) As Integer Dim counter As Integer //Pre: Ningun //Post: Devuelve el índice del arreglo dentro del arreglo Word en el caso de que esté, o bien -1 si no es así.

For counter = 0 to UBound(Word) If Word(counter) = w then return counter End if Next Return –1

Page 45: Curso REALbasic

Asegúrate de que entiendes lo que hace este método1.

5 Añade el siguiente método a la ventana:

Sub AddWord (w As String) //Pre: Los arreglos Count y Word tienen la misma cantidad de elementos//Post: Si w está en el arreglo Word, se incrementa el valor de Count en el mismo índice//de lo contrario, se añade w al arreglo Word, y se incorpora 1 al arreglo de Count//es decir, estamos manteniendo un contador de las palabras enviadas a este método, en los dos arreglosDim position As Integer position = WordPosition(w) If position = -1 then Word.append w Count.append 1 Else Count(position) = Count(position) + 1 End if

Deberías advertir que al dividir WordPosition en una función separada permite que el resto del código sea independiente de la representación de los datos. Es una buena idea mantener certezas como ʻtodos estos caracteres son caracteres de palabrasʼ lejos del código que las utiliza.

6 Añade lo siguiente al manejador de evento Action de CountButton:

//Ejecuta CountWords y copia a continuación los resultados en WordListDim counter As integer CountWords For counter = 0 to UBound(Word) WordList.AddRow Str(Count(counter)) WordList.Cell(counter,1) = Word(counter) Next

Tal y como habrás podido leer en el caso de que hayas consultado el control ListBox en la documentación suministrada de serie, el ListBox permite mostrar información en un listado o rejilla. Entre sus características se encuentra el método AddRow, y que se encarga de añadir una fila y de definir los contenidos de la primera columna; así como la propiedad Cell, y que te permite definir los contenidos de cada una de las celdas de la rejilla. Las dos líneas en el anterior bucle For, por tanto, se encargan de añadir una nueva fila a WordList.

EjecútaloListo. Como de costumbre, deberías ejecutar el programa y probarlo. También deberías probar a insertar puntos de parada y avanzar paso a paso por la ejecución del código para observalo en acción.

Experimenta

1 Ten en cuenta que REALbasic proporciona una función IndexOf que hace lo mismo que esta función para cualquier arreglo. En ciertos casos escribiremos nuestra propia versión de las funciones incorporadas con el único propósito del aprendizaje.

Page 46: Curso REALbasic

Hay muchos modos de ampliar el proyecto desde este punto. Algunas de ellas en orden creciente de dificultad son:

• Cambia el código para que ignore los números (¿recuerdas como detectamos si una cadena es un número de una lección anterior?);

• El código proporcionado no es sensible a la caja (mayúsculas/minúsculas son consideradas del mismo modo cuando se comparan las cadenas), dado que así es como funciona el operador = estándar en REALbasic. Puedes realizar comparaciones que sí tengan en cuenta este factor utilizando para ello la función StrComp. Prueba a añadir una casilla de verificación a la ventana que permita al usuario elegir si quiere que el contador de palabras tenga en cuenta la caja o no;

• Añade un panel independiente para que muestre las palabras más comunes junto con su contador. Hay dos modos obvios de llevarlo a cabo (una vez se ha creado el arreglo, y a medida que se está creando); y

• Puedes probar a que el listado se ordene correctamente cuando se haga clic sobre las cabeceras de las columnas. Esto es realmente sencillo de hacer, pero tendrás que leer la documentación correspondiente al ListaBox para averiguar cómo. La lectura de la documentación es tan importante que deberías comenzar a hacerlo ahora, en cualquier caso.

ConclusiónEl desarrollo de algoritmos es en gran parte de lo que trata la ciencia de la computación, y el desarrollo de un algoritmo tiene que ver más con el arte que con la ciencia. Fundamentalmente es más una cuestión de práctica, y de leer sobre algoritmos para aprender cómo hacerlos. En esta lección hemos intentado proporcionarte algunas líneas maestras y un ejemplo sobre cómo plantear el desarrollo de un algoritmo.

Esta es buena ciencia de la computación, y se puede encontrar en línea una buena cantidad de recursos y discusiones sobre estas y otras virtudes de diseño.

Page 47: Curso REALbasic

Lección 8 Tipos de Datos Abstractos

Objetivo

En esta lección echaremos un vistazo a la programación orientada a objetos. Comenzaremos examinando cómo y por qué deberíamos crear nuestros propios tipos de datos.

¿Qué es un Tipo de Dato Abstracto?

Formalmente, un Tipo de Dato Abstracto (TDA) está compuesto por un conjunto de operaciones que suponen el único modo de que otro código pueda acceder a un dato en particular. El TDA define un tipo, y se dice que el dato es de ese tipo.

La ventaja que proporciona es que se separa la implementación de la interfaz. En términos convencionales de programación orientada a objetos, esto se denomina encapsulado. También puedes oír que el TDA abstrae los detalles de los datos, y que proporciona ocultación de la información.

La encapsulación proporciona las siguientes ventajas:

• Podemos desarrollar y depurar el Tipo de Dato Abstracto independientemente del resto del programa;

• Podemos cambiar la implementación del Tipo de Dato Abstracto sin tener que cambiar cualquier código que lo llame; y

• Podemos proporcionar acceso a varios tipos similares de fuentes de datos desde un único código, sin tener que resolver los detalles encargados de diferenciar entre los tipos de datos.

Estas ventajas suponen sólo un primer atisbo de algo que veremos de forma repetida a lo largo de este curso: queremos ser capaces de descomponer un programa complejo en partes bien definidas que puedan desarrollarse, depurarse, mejorarse y cambiarse independientemente.

La segunda ventaja también nos permite abordar el desarrollo de un modo incremental: podemos implementar un TDA de un modo rápido y sin pulir para tener el programa funcionando en poco tiempo, y encargarnos posteriormente de afinarlo para que sea más rápido, use menos memoria, o proporcione características adicionales.

Pre- y Post-Condiciones

Hemos visto las precondiciones y postcondiciones brevemente en la última lección. Deberías de poder emplear estos conceptos a la hora de definir un Tipo de Datos Abstracto: deberías de indicar de forma lo más simple y precisa posible qué hace cada una de las operaciones proporcionadas por el Tipo de Dato Abstracto, en términos de lo que debería de ser cierto antes de que se ejecute la operación, y qué será diferente después.

ʻOrientado a objetosʼ implica soporte de serie para los TDA

Page 48: Curso REALbasic

Si has utilizado otros lenguajes de programación con anterioridad, deberías saber que puedes emplear las ventajas de los TDA virtualmente en cualquier lenguaje de programación. Sin embargo, los Lenguajes de programación orientados a objetos como REALbasic proporcionan características de serie que soportan de forma explícita la creación de Tipos de Datos Abstractos, tal y como veremos.

Mejorando el Contador de Palabras con TDA

1 Crea una copia del proyecto Contador de Palabras. Ábrelo.Vamos a crear una versión del Contador de Palabras que cuente el texto del Portapapeles. Vamos a hacer que obtenga el texto de un Tipo de Datos Abstracto.

2 En el Editor de Proyecto, haz clic en el botón Añadir Clase.Verás a continuación un nuevo icono en el Editor de Proyecto, y el panel Propiedades estará listo para que configures sus propiedades:

Figura 38. Las Propiedades de la clase CharacterSource.

(Captura Figura 38)

La única propiedad que tienes que cambiar ahora es el Nombre, y que deberías definir como CharacterSource, tal y como se muestra en la Figura 38.

Acabas de declarar un nuevo tip ode dato en el que puedes almacenar en variables y propiedades en tu programa. El siguiente paso consiste en proporcionarle los métodos y propiedades adecuados de modo que podamos usarlo como fuente de datos en nuestro contador de palabras.

3 Haz doble clic en el icono correspondiente a la clase CharacterSource para abrir su Editor de Código.

4 Haz clic en el botón Añadir Propiedad y proporciona a la nueva propiedad el nombre “ClipBoardSource” y el tipo de dato “ClipBoard”:

5 Haz clic de nuevo en Añadir Propiedad y proporciona a la nueva propiedad el nombre “CurrentPosition” y el tipo de dato “Integer”.

Referencias a Objeto frente a EscalaresEsta es la primera vez que hemos almacenado un objeto en una propiedad o variable. El tipo ClipBoard que hemos declarado para nuestra propiedad ClipBoardSource es un objeto, lo que supone un tipo de cosa distinta de las cadenas, los números y los Booleanos que hemos utilizado en las propiedades y variables hasta ahora.

Las propiedades en REALbasic pueden almacenar dos tipos de cosas diferentes: escalares y referencias de objetos. Los escalares son simplemente un valor. Una vez que hayas declarado la variable, puedes comenzar a trabajar con su valor.

Una referencia de objeto es diferente: esta indica a REALbasic donde puede ubicar un objeto en la memoria. Cuando declaras la variable o propiedad sólo has declarado un contenedor en el cual almacenar la ubicación de un objeto (además de haber indicado qué tipo de objeto estará en dicha ubicación), pero no has creado en realidad dicho

Page 49: Curso REALbasic

objeto. Una variable o propiedad declarada para que sea un objeto tiene inicialmente el valor especial de Nil, lo que significa que apunta a ninguna parte en concreto.

Antes de que puedas comenzar a hacer algo con un objeto, debes coger la variable o propiedad para referirte a él. En este caso, necesitamos pedir a REALbasic que cree un objeto, y que prioporcione una referencia a dicho objeto que podamos almacenar en nuestra variable.

Este es el único modo en el que REALbasic puede acceder a los objetos. Aunque puede parece un tanto extraño, es en realidad un modo natural de trabajar con los objetos y pronto resultará algo habitual para ti.

1 Haz clic en el botón Añadir Método, y nombre el nuevo método Begin sin proporcionar parámetros o un valor de retorno. Define su código a:

//Debe llamarse antes de que se pueda utilizar el objetoCurrentPosition = 0 ClipBoardSource = New Clipboard

2 Añade los siguientes métodos a la clase:

Function CurrentChar as String

//Pre: Se ha llamado a Begin dado que se ha modificado el clipBoard;//Este método no es llamado más veces que la longitud del texto del clipBoard//Post: Cada vez que se llama a este método, se devuelven en orden los caracteres del texto clipBoardCurrentPosition = CurrentPosition + 1 Return Mid (ClipBoardSource.Text, CurrentPosition, 1)

Function Finished as Boolean

//Pre: Se ha llamado a Begin //Post: devuelve true en el caso de que la última llamada a CurrentChar haya devuelto el último caracter del texto del clipBoard, de lo contrario devuelve falseReturn (CurrentPosition > Length+1)

Function Length As Integer

//Pre: Se ha llamado a Begin //Post: se devuelve la longitud del texto en el ClipBoardReturn Len (ClipBoardSource.text)

Cada una de estas funciones no requiere ningún parámetro, pero cada una de ellas devuelve un valor.

Para seguir este código, probablemente necesites buscar algunas de las características de REALbasic (como por ejemplo Len y Mid, así como las características del objeto ClipBoard).Ten en cuenta que podemos devolver simplemente los resultados de una expresión Booleana (en la función Finished).

Page 50: Curso REALbasic

¿Puedes ver cómo los métodos que hemos implementado aquí proporcionan todo lo que necesitamos para iterar la cadena del portapapeles? Ten en cuenta también que cualquier código que llame a estos métodos no tiene por qué conocer nada sobre el origen de la cadena.

3 Añade una propiedad a la ventana y nómbrala “ClipBoardSource” y proporciónale el tip de dato de “CharacterSource”.

4 Cambia el parámetro pasado a IsWordCharacter to “c as String” y cambia su código a:

//Pre: Ninguna//Post: Si c es una letra o número, devuelve true, de lo contrario devuelve false

Return (c>="0" and c<="9") or (c>="A" and c<="Z") or (c>="a" and c<="z")

Modifica tu método CountWord para que sea:

//Pre: Ninguna //Post: el arreglo Word contiene todas las palabras en ClipBoardSource;//El arreglo Count contiene el correspondiente contador para dichas palabras en ClipBoardSourceDim InWord As Boolean Dim c, StringBuffer As String // Inicializa ClipBoardSource ClipBoardSource = new CharacterSource ClipBoardSource.Begin // Invariante c, comenzar *antes* del primer caracter InWord = false //Invariante Inword StringBuffer = "" //Invariante StringBuffer while not ClipBoardSource.Finished //Invariantes: // c: c es el siguiente caracter aun no procesado de ClipboardSource// StringBuffer: StringBuffer se encarga de contener la cadena de caracteres que será la próxima palabra que se añada a nuestra lista; una vez añadida, o entre palabras, esta cadena está vacía.//Inword: es true si el contador de caracter de Source.text fue IsWordCharacter//Note: A diferencia de StringBuffer, sólo necesitamos actuar sobre los límites de la palabrac = ClipBoardSource.CurrentChar //invariante c if InWord then if not IsWordCharacter (c) or ClipboardSource.Finished then //fin de palabra; envía la palabra AddWord StringBuffer //invariante StringBuffer StringBuffer = "" //invariante StringBuffer InWord = false //invariante InWord else StringBuffer = StringBuffer + c end if else //no está en InWord If IsWordCharacter (c) then //WordBegin; comienza el buffering InWord = true //invariante InWord StringBuffer = StringBuffer + c

Page 51: Curso REALbasic

if ClipBoardSource.Finished then AddWord StringBuffer end if end if end if Wend

Aquí puedes ver que llamamos a los métodos y funciones desde las clases que creamos, exactamente tal y como hacemos con las clases de serie.

Es importante reconocer la diferencia entre el tipo o clase (CharacterSource) y una instancia de dicho tipo (ClipBoardSource). En la vida real existe una analogía: Tom Cruise es una instancia de la clase actor; o New York es una instancia del tipo ciudad. Los programadores de ordenadores también pueden utilizar un tipo de atajo y decir que ʻNew York es una instancia de cityʼ.

El Bucle While

Anteriormente hemos introducido un nuevo tipo de bucle: un bucle While. Tiene una forma similar de un bucle For, pero empieza con la instrucción While — consiste de la palabra While seguida de una condición Booleana — y finaliza con la palabra Wend. Comprueba la condición, ejecuta el código que se encuentra dentro del bucle, y repite estos dos pasos una y otra vez hasta que la condición sea falsa.

¿En qué medida supone el código del TDA una mejora?

Hemos cambiado la fuente del texto para contar las palabras, pero eso no constituye una mejora: es un cambio de característica. ¿En qué medida es la escritura de este contador de palabras una mejora?

Cuando escribimos código de ordenador, buscamos por lo general estas cosas:

• Implementar correctamente el comportamiento deseado;• Implementar el comportamiento deseado, totalmente depurado y lo más rápido posible; y• Hacer que el código sea lo más sencillo de modificar como podamos.

En algunos casos2 también queremos:

• Hacer que el código se ejecute utilizando la mínima cantidad de recursos (tiempo, memoria, ancho de banda, etc).

El código de este proyecto es una mejora sobre el código del último proyecto porque satisface en mejor medida el requerimiento de sencillez en la modificación. Este beneficio no es aparente de forma inmediata, pero en las próximas lecciones veremos cuan sencillo resulta ahora conseguir que nuestro contador de palabras funcione con diferentes fuentes de datos.

Este es un principio de diseño general muy bueno: cualquier estructura de datos que crees debería tener una capa de código de interfaz. Este código debería proporcionar las

2 Es un error muy común entre los programadores obsesionarse sobre la “eficiencia” en una buena cantidad de situaciones donde simplemente no importa.

Page 52: Curso REALbasic

características necesarias por el resto del programa para utilizar dichos datos, pero debe ocultar todos los detalles sobre cómo se almacenan realmente dichos datos.

La ventaja de los TDAEs importante ver en este punto lo que hemos logrado utilizando un TDA para este proyecto: CountWords no necesita saber nada sobre la fuente de las palabras que está contando. Al contrario, hemos reducido al mínimo las características necesarias para que haga su trabajo. Ahora, podemos contar las palabras en cualquier fuente de texto implementando una interfaz muy sencilla para dicho texto. No necesitamos modificar CountWords en absoluto. Podemos, en cierto sentido, ʻenchufarʼ una fuente de texto a CountWords. En este caso, se trata del texto de un Portapapeles.

En las próximas lecciones veremos el modo tan claro en el que la programación orientada a objetos soporta esta capacidad de “encadenar las cosas”.

Un ejercicioHay otra estructura de datos en nuestro programa: la pareja de arreglos que mantiene las palabras y sus contadores. Prueba a implementar esto como un TDA.

Page 53: Curso REALbasic

Lección 9 Polimorfismo

Objetivo

En esta lección comenzarmos a aprender cómo utilizar el polimorfismo, capacidad que nosp ermite sacar provecho de las similitudes entre ojetos para simplificar nuestro código.

Cómo utilizar esta lección

Es probable que te encuentres volviendo a esta lección en varias ocasiones. Algunas de ellas pueden parecer extrañas o contraintuitivas la primera vez que las leas.

¿Qué es el Polimorfismo?El Polimorfismo, de un modo general, es la capacidad para tratar a cosas similares de formas también similares. Así es como funciona en REALbasic (menos uno o dos detalles que aprenderemos en lecciones posteriores):

• Como hemos vistao en la última lección, todos los objetos tienen una clase;• Las clases están ubicadas en una jerarquía de clase con forma de árbol (cada clase

tiene una superclase y tantas subclases como sean precisas);• Las clases heredan todos los métodos y propiedades de su superclase;• Cada clase es su propio tipo, pero las clases también son del tipo de su superclase, la

superclase de su superclase, la superclase de esta, y todas las superclases hasta que se alcance la clase raíz (root) de toda la jerarquía de clases, la clase objeto.

• Puedes tratar un objeto como una instancia de cualquiera de sus tipos. Esto significa, por ejemplo, que peudes tratar todos los controles de una ventana como objetos RectControl, dado que todos los controles son subclases del objeto RectControl. Por ejemplo, puedes cambiar la posición de todos ellos sin necesidad de que debas escribir código diferente para manejar cada uno de los diferentes tipos.

Esta última capacidad es el polimorfismo: podemos introducir comportamientos compartidos en una superclase compartida y escribir el código encargado de trabajar sólo con las características compartidas de los objetos, ignorando de este modo sus diferencias.

Utilizado con propiedad, el polimorfismo nos permitirá reducir la cantidad de código que debamos escribir y depurar, así como reducir la cantidad de trabajo requerido para mantener y ampliar un programa. Sin embargo, el polimorfismo es una característica sutil y aprender a utilizarlo bien requiere tanto de experiencia como de una buena comprensión de la misma. Gran parte de lo que vamos a hacer en este curso es proporcionarte las pinceladas en el uso efectivo de la programación orientada a objetos, y gran parte de ello tiene que ver con el uso eficaz del polimorfismo.

Nos llevará varias lecciones cubrir todas las características de programación orientada a objetos de REALbasic. En esta lección, aprenderemos las jerarquías de clases, y también el uso de eventos para ampliar una superclase. También modificaremos el contador de palabras de las anteriores lecciones de modo que pueda funcionar con diferentes fuentes de texto sin necesidad de realizar modificaciones adicionales.

Un ejemplo de jerarquía de clase

He aquí el diagrama de parte de una jerarquía de clase interna de REALbasic:

Page 54: Curso REALbasic

Figura 39. Parte de una jerarquía de clase interna.

(captura Figura 39)

Un par de ejemplos de este diagrama: Object es una superclase del resto de las clases; Control es una subclase de Object, y una superclase para Line, RectControl, PushButton y TextField.

En ciertos casos, puede ser preciso distinguir la superclase inmediata de una clase: RectControles la superclase inmediata de un PushButton, por ejemplo. En otros casos, la superclase inmediata de una clase sólo será llamada por su superclase. En significado exacto que tenga superclase debería de ser obvio por el contexto.

Nuestra jerarquía de claseComo ejemplo adicional, esta es la jerarquía de clase que vamos a crear para esta lección:

Figura 40. La jerarquía de clase para este proyecto.

(Captura Figura 40)

El contador de palabras polimórficoVeamos un ejemplo funcional:

1 Abre el proyecto de la última lección.¿Recuerda que escribimos el contador de palabras para que obtuviese sus datos a partir de un Tipo de Datos Abstracto? Vamos a modificar nuestro proyecto de modo que dicha fuente de datos sea un objeto de una clase concreta. A continuación haremos que el método contador de palabras pueda obtener la información a partir de una subclase de dicha clase.

2 Abre la clase CharaterSource. Vamos a cambiarla para que no haga nada. En serio.

Herencia, Polimorfismo y Clases AbstractasVamos a convertir CharacterSource en una clase base Abstracta. Su papel consistirá en definir una interfaz para un Tipo de Dato Abstracto. Utilizaremos subclases para proporcionar el comportamiento real deseado. No querrás crear un objeto CharacterSource como tal (dado que no haría nada). Crearás un objeto de una de las subclases de CharacterSource, pero nuestro método CountWord tratará dichos objetos como CharacterSource y no necesita saber qué objeto es en realidad.

1 Añade cuatro definiciones de evento a nuestra clase.Para ello haz clic en el botón Añadir Definición de Evento para cada una de las definiciones de evento. Esta acción abre una área de declaración con las mismas opciones que tienes para crear un nuevo método. Nombra los cuatro eventos como sigue:

Nombre de evento Tipo devuelto

BeginEvent (ninguno)

Page 55: Curso REALbasic

Nombre de evento Tipo devuelto

CurrentCharEvent String

FinishedEvent Boolean

LengthEvent Integer

EventosLas Definiciones de Eventos proporcionan un medio para que el código definido en la superclase llame al código definido en la subclase, sin necesidad de que deba saber nada sobre la subclase.

Lo que vamos a hacer ahora es modificar los métodos en esta clase para que no hagan nada más que llamar a los eventos que acabamos de definir. A continuación crearemos dos subclases, una para que devuelva texto del Portapapeles y otra para que devuelva texto de un archivo.

2 Borra la propiedad ClipBoardSource de CharacterSource.Puedes seleccionar cada propiedad en el área de navegación del Editor de Código y hacer clic secundario (Comando + clic en el Macintosh) seleccionando a continuación Borrar en el menú contextual.

3 Modifica los métodos en CharacterSource para que sea:

Function CurrentChar() As String Return CurrentCharEvent()

Function Finished() As Boolean Return FinishedEvent()

Function Length() As Integer Return LengthEvent()

4 Cambia al Editor de Proyecto y haz clic en el botón Añadir Clase. Utiliza ClipboardSource como nombre de la nueva clase e introduce CharacterSource como su Super.

5 Haz doble clic sobre la clase para acceder a su Editor de Código. Despliega los Manejadores de Evento en el panel de la izquierda.Podemos ver los cuatro eventos definidos automáticamente aquí dado que esta clase es una subclase de CharacterSource.

6 Añade la propiedad que hemos eliminado de CharacterSource:ClipBoardSource As Clipboard

7 Introduce el siguiente código en los cuatro manejadores de Evento BeginEvent:

//Pre: Ninguno //Post: CurrentPosition == 0 CurrentPosition = 0

Page 56: Curso REALbasic

ClipBoardSource = New Clipboard

CurrentCharEvent//Pre: Se ha llamado a Begin dado que se ha modificado el clipBoard;// Este método se llama tantas veces como la longitud del texto del clipBoard//Post: cada vez que se llama a este método, se devuelven en orden los caracteres del clipBoard CurrentPosition = CurrentPosition + 1 Return Mid (ClipBoardSource.Text, CurrentPosition, 1)

FinishedEvent//Pre: Se ha llamado a Begin //Post: Devuelve true en el caso de que la llamada a CurrentChar haya devuelto el último caracter del texto del clipBoard, de lo contrario falseReturn (CurrentPosition > Length)

LengthEvent//Pre: Se ha llamado a Begin //Post: se devuelve la longitud del texto en el clipBoardReturn Len (ClipBoardSource.text)

8 Modifica Window1 para que tenga este aspecto:

Figura 41. La ventana del Contador de Palabras.

(Captura Figura 41)

9 Cambia el nombre del PushButton a “CountClipButton” y Caption a “Count Clipboard”.

Ahora, necesitamos modificar CountWord para que funcione con el CharacterSource proporcionado como argumento. Cambia su declaración de modo que sea pasado como parámetro, “Source as CharacterSource”.:

//Pre: Ninguno //Post: El arreglo Word contiene todas las palabras de Source;// El arreglo Count contiene los contadores correspondientes para las palabras de SourceDim InWord As Boolean Dim c, StringBuffer As String Source.Begin // Invariante c, comenzando *antes* del primer caracter InWord = False //Invariante Inword StringBuffer = "" //Invariante StringBuffer While Not Source.Finished //Invariantes: // c: c es el siguiente caracter aun no procesado en Source// StringBuffer: StringBuffer contiene la cadena de caracteres que formará la próxima palabra añadida al listado; una vez añadida, o entre palabras, esta cadena está vacía.// Inword: InWord is true si el contador de caracteres de Source.text fue IsWordCharacter//Note: A diferencia de StringBuffer, sólo necesitamos actuar sobre los límites de la palabrac = Source.CurrentChar //invariante c if InWord then

Page 57: Curso REALbasic

if not IsWordCharacter (c) or Source.Finished then //Fin de palabra; enviamos la palabra AddWord StringBuffer //Invariante StringBuffer StringBuffer = "" //Invariante StringBuffer InWord = false //Invariante InWord else StringBuffer = StringBuffer + c end if else //no InWord If IsWordCharacter (c) then //WordBegin; comenzamos con el buffering InWord = true //Invariante InWord StringBuffer = StringBuffer + c if Source.Finished then AddWord StringBuffer end if end if end if Wend

10 Define el manejador de evento Action en el botón Count Clipboard a:

//Ejecuta CountWords en el clipboard, y copia a continuación los resultados en WordListDim counter As integer Dim Source As ClipBoardSource Source = New ClipboardSource CountWords(Source) For counter = 0 to UBound(Word) WordList.AddRow str(Count(counter)) WordList.cell(counter,1) = Word(counter) Next

11 Define un punto de ruptura en la parte superior del manejador de evento Action recién creado, introduce algún framento de texto en el Portapapeles (será suficiente con unos cuantos caracteres), ejecuta el programa y avanza por el proceso de ejecución del código en las clases CharacterSource y ClipboardSource.Verás que los métodos que llama CountWords desde su argumento Sourcevan en primer lugar a los métodos de CharacterSource, y que después ʻbajaʼ a los manejadores de eventos en la subclase ClipboardSource. Las funciones devuelven sus resultados en el orden opuesto: primero a la función CharacterSource que la llamó y después a CountWord desde el que se llamó originalmente el método.

Clases frente a ObjetosEs importante ser claro sobre la terminología: la clase es la definición de lo que hace el objeto; el objeto es la ʻcosaʼ realmente usable que creamos cuando se ejecuta nuestro programa. El objeto contiene datos y hace las cosas definidas por la clase. Decimos que un objeto es una instancia de la clase.

Un objeto siempre es una instancia de más de una clase: en este caso, Source es una instancia tanto de ClipboardSource como de CharacterSource. Dentro del manejador de evento Action del botón, lo tratamos como un ClipboardSource, dado que necesitamos crear una clase totalmente funcional y no abstracta que haga cosas realmente. Sin embargo, CountWord tiene una variabla CharacterSource como su argumento. Cuando llamamos a CountWord, su argumento Source termina apuntando al mimso objeto que

Page 58: Curso REALbasic

hemos creado (¿recuerdas nuestra discusión sobre cómo las variables de objeto no contienen un objeto, sino que sólo se refieren a ellos?).

Polimorfismo en acciónEsto es polimorfismo en acción: podemos pasar a CountWord cualquier clase que tenga CharacterSource como su superclase, sin necesidad de que debamos modificar el método CountWords. Cualquiera de los detalles requeridos para que funcione con una fuente de datos en particular serán proporcionados en los manejadores de eventos de la subclase CharacterSource en cuestión.

Esto significa que podemos depurar CountWords como un elemento aislado, reduciendo así la cantidad de código que necesitamos escribir para contar las palabras provenientes de una nueva fuente de datos. Veremos eso justo ahora en el código que implementemos como comportamiento del botón Count File...

Más sobre EventosLos eventos suponen el mecanismo más imporante de extensión en REALbasic. Los eventos que definas e interceptes en las clases son el mismo mecanismo que los manejadores de eventos que hemos estado escribiendo para los controles. Por ejemplo puedes crear tu propia clase de botón especializada, creando para ello una nueva clase y definiendo su Super como PushButton y proporcionando a continuación el código en su manejador de evento Action o de otros eventos. También puedes definir nuevos eventos en tu nueva clase y que estarán disponibles para cualquier subclase futura, así como para cualquier instancia de dicho botón que arrastres desde la ventana de proyecto hacia la Ventana creada.

Los eventos, como mecanismo de ampliación, son únicos de REALbasic. Otros lenguajes de programación otientados a objetos utilizan mecanismos de extensión muy diferentes denominados sobreescritura de métodos. La sobreescritura de métodos también está disponible en REALbasic, y aprenderemos sobre esto en posteriores lecciones. No busques mucho consejo sobre cómo utilizar los eventos o bien sobre cuándo utilizar los eventos en vez de la sobreescritura de métodos. Aparte de lo que vayamos a desarrollar en este curso, y de lo que puedas encontrar en Internet por parte de otros programadores de REALbasic, o bien en los escasos libros sobre REALbasic, no hay mucha información al respecto.

El Contador de ArchivosAhora, vamos a crear el código para que funcione el botón Count File... Como de costumbre, y en el caso de que lo necesites, consulta la información relativa a la gestión de archivos en REALbasic utilizando la referencia incorporada.

1 En el Editor de Proyecto, crea una nueva clase. Nómbrala FileSource y define su Super a CharacterSource.

2 Abre el Editor de Código de la clase y añade las siguientes propiedades:

Nombre Tipo de Dato

CurrentPosition Integer

TheStream BinaryStream

Page 59: Curso REALbasic

3 Añade el siguiente código a los Manejadores de Eventos:

BeginEventCurrentPosition = 0 TheStream.position = 0

CurrentCharEventCurrentPosition = CurrentPosition + 1 Return Chr(TheStream.ReadByte)

FinishedEventReturn CurrentPosition > Length

LengthEventReturn TheStream.Length

Un problema menorEn el código anterior escribimos nuestro propio medio para detectar el final de un archivo (manteniendo un contador de CurrentPosition, y comparándolo con la longitud del archivo). El BinaryStream proporciona tanto la propiedad Position como la propiedad EOF, pero el modo en el que están implementadas no nos sirven a nuestro propósito. EOF es True antes de que hayamos leído el último caracter, y Position será igual a la longitud del archivo tanto antes como después de haber leído el último caracter. De modo que no podemos escribir un bucle While apropiado basándonos en cualquiera de estas propiedades. Por ejemplo, si escribiésemos:

Function FinishedEvent() As Boolean Return TheStream.EOF

No podríamos devolver el último caracter del archivo. Si escribiésemos:

Function FinishedEvent() As Boolean Return TheStream.Position = TheStream.length

Tendríamos el mismo problema. Pueden buscarse otros modos de utilizar estos mecanismos incorporados para leer el último caracter, pero no sin hacerlo excesivamente complicado como para que no falle cuando intentemos leer un CharacterSource cuya longitud sea de cero caracteres (¡inténtalo!).

Sin embargo, este no es un gran problema; sólo hemos de escribir nuestro propio mecanismo.

1 Añade el siguiente método a la clase FileSource:

Sub SetFile(f As BinaryStream) TheStream = f

2 Añade el siguiente código al manejador de evento Action para el botón Count File...:

//Solicita un archivo al usuario y ejecuta a continuación CountWords para dicho archivo,//y copia los resultados en WordList

Page 60: Curso REALbasic

Dim Counter As Integer Dim Source As FileSource Dim f As FolderItem Dim s As BinaryStream Source = new FileSource //Solicita el archivof=GetOpenFolderItem("application/text") If f<>Nil then s = f.OpenAsBinaryFile(False) Source.SetFile s CountWords(Source) For Counter = 0 to UBound(Word) WordList.AddRow str(Count(counter)) WordList.cell(counter,1) = Word(counter) Next End if

Advierte como hemos añadido un método adicional a nuestra clase. Los objetos del tipo CharacterSource garantizan el soporte de cuatro métodos (Begin, CurrentChar, Finished y Length), pero los objetos del tipo FileSource soportan un método adicional (SetFile). Observa que el botón sabe que está trabajando con un FileSource, de modo que puede definir el archivo, pero CountWords no sabe nada sobre este método adicional, y tampoco necesita saberlo.

Tipos de ArchivoNecesitamos indicar a REALbasic qué tipos de archivos intentamos abrir. Deberías leer el Capítulo 8 de la Guía del Usuario para obtener información adicional sobre los Tipos de Archivo.

1 Selecciona Proyecto > Añadir > Grupo de Tipo de Archivo.Verás un nuevo icono FileTypes en la pestaña Proyecto. Haz doble clic sobre él para acceder al Editor de Tipos de Archivo:

Figura 42. El editor de Grupo de Tipo de Archivo.

(Captura Figura 42)

2 Haz clic sobre el botón Añadir Tipo de Archivo para añadir una nueva fila al listado. Introduce la siguiente información:

Campo Valor

Muestra Nombre application/text

Nombre de Objeto applicationText

MacType TEXT

MacCreator ????

Page 61: Curso REALbasic

Campo Valor

Extensiones .1st;.nfo;.readme;readme;.txt

PruébaloListo. Probablemente quieras trazar la lectura de un archivo (muy pequeño).

RevisiónHemos visto los aspectos básicos en la implementación de una jerarquía de clase, así como del empleo del polimorfismo. Deberías de volver atrás y leer de nuevo el material introductorio que trataba las características utilizadas en este programa. Centrate en la jerarquía de clase y en el mecanismo de eventos que nos permite tratar dos clases diferentes como si fueran la misma. Advierte como se trata de una extensión de la idea correspondiente al Tipo de Dato Abstracto.

Diseño Orientado a ObjetoUn tema principal de este curso está relacionado sobre cómo diseñar aplicaciones cuyas partes claramente separadas nos permitan desarrollar y depurar de forma independiente partes individuales de programas complejos. Hemos visto cómo los métodos y los Tipos de Datos Abstractos nos ayudan a realizar dicha labor. La herencia y el Polimorfismo llevan dicha idea un paso más allá: no sólo puedes implementar diferentes TDA de forma separada, sino que también puedes desarrollar y depurar comportamientos compartidos entre los ojetos que implemente el mismo TDA una vez. Los buenos diseños orientados a objeto tendrán con frecuencia una buena cantidad de este tipo de código situado en la parte superior de las jerarquías de clase, lo que significa que una buena cantidad del código del programa es generalizado y compartido.

Alcanzar este tipo de eficiencia en el diseño tiene lo mismo de arte que de ciencia. Son precisos años de experiencia en programación y una buena cantidad de código otientado a objeto bien escrito para desarrollar este nivel de aptitud en el diseño orientado a objeto.

Explicaciones/ReferenciasLos eventos están tratados en el Capítulo 5 y en la sección Añadir Definiciones de Eventos del Capítulo 9 en la Guía del Usuario de REALbasic.

Desafortunadamente, la Guía del Usuario de REALbasic sólo trata el polimorfismo usando el otro mecanismo (ʻmétodos virtualesʼ que trataremos posteriormente) Pero ten en cuenta que si lees la sección Añadir Nuevos Eventos en el Capítulo 9, lo que está describiendo es polimorfismo.

Estamos tratando estas cuestiones a pequeños sorbos, de modo que habrá que esperar para obtener una buena comprensión de estos temas hasta que hayamos realizado previamente algunas lecciones adicionales.

Uno de los principios de diseño importantes a los que queremos adherirnos es que las superclases no deberían de saber nada sobre sus subclases. Al definir un evento y llamarlo, una superclase está definiendo un modo de comunicarse con su subclase, sin importar cuál pueda ser, al tiempo que consigue mantener su ignorancia sobre los detalles de dicha clase.

Page 62: Curso REALbasic

Si lees el Capítulo 9, este puede ayudarte a entender que el mecanismo de Métodos Virtuales descrito es otro modo de conseguir que se produzca una comunicación entre la subclase y la superclase (pero en este caso, la subclase es la encargada de mantener el control). Hemos empezado con los eventos, dado que los necesitas para crear subclases de los controles, y de hecho permaneceremos con los eventos durante algún tiempo más dado que queremos mantener las cosas simples por el momento.

Ejercicio AdicionalPrueba a añadir una TextBoxSource que cuenta los contenidos de un TextField. Ten en cuenta que el TextField ya es una clase, y dado que sólo puedes definir una superclase para una clase, no puedes crear una subclase de TextField que también lo sea de CharacterSource. En vez de ello tendrás que escribir algún tipo de clase adaptadora que incorpore el método SetTextBox, similar al SetFile de FileSource.

Page 63: Curso REALbasic

Lección 10 Eliza

ObjetivoEn esta lección crearemos un programa con el que pondremos en práctica nuestros recién adquiridos músculos de programación. Continuaremos siendo fieles al tipo de cosas que ya hemos venido utilizando hasta ahora, sin aventurarnos en nuevo material relacionado con los TDA y con el polimorfismo. Sólo estamos intentando ver qué aspecto tiene un programa ligeramente más complejo.

¿Qué es Eliza?Eliza es un programa clásico de ordenador. El programa pretender dialogar con el usuario, actuando como un psiquiatra o consejero. El usuario escribe texto en inglés (o castellano) y el programa parece responder de forma inteligente también en inglés (o en castellano).

Planificando el programaDebemos implementar varias cosas diferentes para asegurarnos de que el programa funciona. Necesitamos proporcionar:

• Un modo de obtener las frases que buscamos y las respuestas correspondientes en dicho programa;

• Almacenamiento y recuperación para cada una de las frases y de las respuesas; y• La interfaz de usuario y la lógica que implementa las preguntas y respuestas reales.

Intenta decidir cómo harías estas cosas antes de continuar.

Cargando frases de un archivoCargaremos las frases desde un archivo de texto que resida en disco. Ya hemos visto antes cómo leer un archivo, pero en esta ocasión tenemos que almacenar alguna información estructurada en el archivo.

El archivo podrá contener múltiples respuestas. Cada respuesta consistirá de una o más palabras o frases para cada una de ellas, y de una o más posibles respuestas.

Esto significa que debemos separar:

• Cada respuesta de la siguiente;• En cada una de las repuestas, las frases a buscar para cada una de las respuestas; y• Dentro de las frases y las respuestas, los términos buscados de forma individual o

respuestas para cada una de ellas.

Llevaremos esto a cabo teniendo cada respuesta en una línea separada dentro del archivo. A continuación separaremos las frases de búsqueda de las respuestas mediante un tabulador. Por último, separaremos cada una de las frases de búsqueda de la otra, y cada una de las posibles respuestas entre sí mediante una barra inclinada (/). De modo que cada una de las líneas del archivo podría tener el siguiente aspecto (el gran espacio en la línea es donde se encuentra el tabulador):

mum/mom/mother Tell me more about your mother./What does this tell us about your mother?

Page 64: Curso REALbasic

Indicaremos las respuestas aleatorias que daremos cuando no exista una coincidencia, mediante una línea que esté vacía antes del tabulador.

Caracteres ASCII y Unicode

Los ordenadores representan las letras como números. Hay dos formas de hacer esto en REALbasic:

• Usando el conjunto de caracteres ASCII (American Standard Code for Information Interchange), un conjunto de 127 caracteres entre los que se incluyen las letras mayúsculas y minúsculas, los numeros, la puntuación y otras, incluyendo algunos caracteres de “control” (como el tabulador); y

• Utilizando el conjunto de caracteres Unicode, un conjunto de caracteres mucho más amplio dirigido a aglutinar cada uno de los símbolos preciso para representar cada lenguaje existente en la Tierra, así como una gran cantidad de otros símbolos.

El ASCII es un viejo estándar. Es importante por dos motivos: 1) compatibilidad con otros sistemas; y (2) porque los caracteres ASCII caben en un único byte3.

Unicode es un estándar más reciente para los principales sistemas informáticos. Es importante porque la mayoría de la gente del mundo necesita más que simplemente el conjunto de caracteres Roman (o Latino) para comunicarse. La única desventaja significativa del Unicode es que requiere del uso de dos bytes para representar cada uno de los caracteres, de modo que precisa el doble de espacio para almacenar el texto en Unicode, y también es un poco más lento a la hora de procesarlo.

Unicode ha sido diseñado de modo que los primeros 127 caracteres sean los mismos que en el conjunto de caracteres ASCII.

REALbasic almacena internamente los caracteres en ASCII si el sistema operativo en el que está funcionando utiliza los caracteres Latino (Roman), y Unicode en los otros sistemas. Sus operaciones de cadenas normales operan sobre ASCII o Unicode en consecuencia. También existen versiones ʻBʼ de todas las operaciones (AscB, etc) que funcionan sólo sobre los bytes. Consulta la referencia incluida de serie para obtener más información.

Para nuestros propósitos, Sólo necesitamos ser capaces de identificar dos caracteres no imprimibles, algo que haremos utilizando la función Chr (y que deberías consultar).

StringResponderComencemos. Dado que se trata de un programa bastante sencillo, pondremos la mayoría de las características en una única clase.

1 Crea un nuevo proyecto. Añade una nueva clase, sin asignar una Super y cuyo nombre sea “StringResponder”. Abre su Editor de Código.

2 Añade las siguientes propiedades:

3 Para los no iniciados, un byte es la unidad estándar de espacio de almacenamiento en un ordenador. Consiste de ocho dígitos binarios (bits; cada uno de los cuales puede ser 1 o 0), y es capaz de representar cualquier de los 256 posibles valores (digamos, los números en un rango de 0 a 255). Se han empleado varios esquemas para lograr que los bytes representen números más grandes, fracciones y texto.

Page 65: Curso REALbasic

Nombre Tipo de Dato

RandomResponse() String

RespondWith() String

SearchFor() String

3 Crea un nuevo método y nómbralo “LoadFromFile”, utilizando como parámetros “f as FolderItem, ElDelimiter as String, RespDelimiter as String”). No tiene tipo devuelto.

4 Introduce el siguiente código en LoadFromFile.

//Pre: f es un folderitem válido de texto, delimitado tal y como se describe a continuación.//Post: Los registros, delimitados por Chr(13), se cargan en las respuestas, //con la respuesta delimitada de la cadena buscada por//estar delimitada mediante RespDilimiter, y múltiples respuestas o búsqueda//delimitadas por ElDelimiter//Ten en cuenta que las Respuestas Aleatorias están indicadas mediante búsquedas vacíasDim InputFrom As TextInputStream Dim InputLine, searchForIn, RespondWithIn As String Dim Counter1, Counter2 As Integer InputFrom = f.OpenAsTextFile //Divide searchForIn de RespondWithInWhile not InputFrom.EOF InputLine = InputFrom.ReadLine searchForIn = Trim(NthField(InputLine, RespDelimiter, 1)) RespondWithIn = Trim(NthField(InputLine, RespDelimiter, 2)) If searchForIn <> "" then //Sperar los múltiples searchForIns, RespondWithIns y los emparejafor Counter1 = 1 to CountFields(searchForIn, ElDelimiter) for Counter2 = 1 to CountFields(RespondWithIn, ElDelimiter) AddResponse NthField(searchForIn, ElDelimiter, Counter1),_ NthField(RespondWithIn, ElDelimiter, Counter2) next //Counter2 next //Counter1 else //Vacía searchForIn, para hacer algunas aleatorias//Añade todas las RespondWithIns a RandomResponsefor Counter2 = 1 to CountFields(RespondWithIn, ElDelimiter) AddRandom NthField(RespondWithIn, ElDelimiter, Counter2) next //Counter2 end if //searchForIn Wend InputFrom.Close

Deberías echar un vistazo a las funciones que no hemos visto antes, como por ejemplo NthField y Trim.

Page 66: Curso REALbasic

Este método puede parecer largo, pero los comandos de cadenas de REALbasic ren realidad hacen que sea bastante directo: separamos las dos partes principales de la línea, y a continuación nos movemos por cada una de las partes. Separamos el almacenamiento de la información en métodos como por ejemplo AddResponse.

5 Añade los siguientes métodos:

AddResponse (S as String, R as String)

SearchFor.Append S RespondWith.Append R

AddRandom(R as String)

RandomResponse.Append R

6 Lo último que necesitamos en nuestra clase es el método qu ese encarga de obtener la respuesta:

FindMatch(S as String) as String

//Pre: Existe como mínimo un elemento en RandomResponse//Post: Busca todos los elementos de SearchFor que estén en S, y devuelve una coincidencia aleatoria que concuerde con la cadena RespondWith//Si no se encuentran correspondencias, devuelve una cadena aleatoria RandomResponseDim Matches() As Integer Dim Counter As Integer //Crea la lista de coincidenciasFor Counter = 0 to UBound(SearchFor) if Instr(S, SearchFor(Counter)) <> 0 then Matches.Append Counter end if Next //Devuelve una coincidencia aleatoria o una respuesta aleatoriaIf UBound(Matches) <> -1 then //Random Match Return RespondWith(Matches(RandomInteger(UBound(Matches)))) Else //Respuesta Aleatoria Return RandomResponse(RandomInteger(UBound(RandomResponse))) End if

Este método busca en la lista de palabras de búsqueda, y en el caso de que se encuentre una, se añade su índice al arreglo Matches. Al final del proceso elegimos uno de los índices del arreglo de forma aleatoria, y devolvemos la respuesta coincidente. Si el arreglo de coincidencias está vacío entonces devolvemos una de las cadenas de RandomResponse.

Ahora sólo tenemos que unir el resto. En primer lugar, necesitamos completar la Function RandomInteger(Top As Integer) As Integer utilizada por FindMatch. Lo añadiremos a un módulo (¿recuerdas los módulos?).

Page 67: Curso REALbasic

7 Crea un nuevo módulo. Nómbralo “Random” y añade la función “RandomInteger” con el parámetro “Top as Integer” y con un tipo devuelto de Integer. Introduce este código.

//Devuelve un entero aleatorio en el rango de 0.. Top

Return round(rnd*Top)

Necesitamos una función que nos proporcione un número aleatorio entre 0 y un número dado. La función Rnd de REALbasic devuelve una fracción entre 0 y 1. Puedes utilizar el número rnd como equivalente de “uno entre todos” para una cantidad determinada, de modo que al multiplicarlo por top y redondearlo al número entero más cercano, obtenemos el valor que queremos.

8 Ahora necesitamos crear nuestra interfaz de usuario. Abre Window1 y configura una interfaz de usuario como esta:

Figura 43. La ventana Eliz.

(Captura Figura 43)

El TextArea de mayor tamaño tiene como nombre Discussion; el más pequeño se llama Entry. Desactiva la propiedad Enabled correspondiente a Discussion en el panel de Propiedades.

9 Abre el editor de código correspondiente a la ventana. Añade una propiedad: Eliza As StringResponder.

10 Añade los siguientes métodos:

HandleEntry

//El usuario ha introducio algo. Manéjalo.Discussion.Text = Eliza.FindMatch(Entry.Text) Entry.Text = ""

SetResponder(R as StringResponder)

Eliza = R

La razón por la que ponemos esto en métodos en vez de hacerlo simplemente en manejadores de eventos es porque permitiremos que el usuario introduzca lo que haya escrito de varias formas.

11 Introduce lo siguiente como manejador de evento KeyDown para Entry:

Dim K As Integer K = Asc(Key) If Key = Chr(13) or Key = Chr(3) then HandleEntry Return True Else

Page 68: Curso REALbasic

Return False End if

Esto permite que el usuario introduzca lo que haya tecleado pulsando las teclas Enter o Retorno.

12 Añade la llamada de método “HandleEntry” al manejador de evento Action del botón.

Ahora sólo queda una cosa por hacer: tenemos que crear un StringResponder y cargar sus contenidos cuando se inicie la aplicación.

13 En el Editor de Proyecto, haz doble clic sobre la clase App y añade el siguiente código en su manejador de evento Open:

//Inicializa Eliza desde archivoDim f As FolderItem Eliza = New StringResponder f = new FolderItem f = f.Child("Responses.txt") If f<>Nil then Eliza.LoadFromFile f, "/", chr(9) Else MsgBox "Error: No se ha encontrado Responses.txt." Quit End if Window1.SetResponder Eliza

Deberías detenerte y leer los componentes nuevos, en particular la propiedad child de los FolderItems.

Un nuevo FolderItem comienza apuntando a la carpeta en la que reside la aplicación (o cuando estás ejecutando desde el IDE, la carpeta en la que hayas guardado el proyecto), de modo que este código intentará cargar un archivo llamado Responses.txt desde esa misma carpeta.

14 Añade la siguiente propiedad a la clase App:

Eliza As StringResponder

Ámbito de los Identificadores

Es posible que hayas advertido que hemos definido dos propiedades de idéntico tipo y nombre (Eliza As StringResponder tanto en la clase App como en Window1). Es importante comprender que se tratan de dos propiedades independientes, y que se pueden referir potencialmente a dos objetos distintos (aunque en este caso terminen refiriéndose al mismo objeto).

Esto funciona porque todos los identificadores (el término de ordenador utilizado para los nombres en un programa informático) de un programa tienen un ámbito (lo que significa la parte del programa en la que resulta válido dicho identificador). Cuando te refieres a un nombre en tu código, se busca el significado del nombre siguiendo este orden:

Page 69: Curso REALbasic

• Variables locales, definidas y válidas sólo dentro del mismo método, declaradas mediante un comando Dim;

• Propiedades, métodos o nuevos eventos visibles en el mismo Editor de Código (lo que significa en la misma clase o ventana);

• Propiedades o métodos en una superclase (en el caso de que no se hayan definido como Private); y

• Propiedades Globales o Públicas o métodos en un módulo o propiedades públicas o métodos en otras ventanas y clases.

Esto significa que el identificador definido en la parte superior de esta lista evitará que el código pueda usar un identificador que de otro modo pudiese ser visible en la zona inferior de la lista.

De igual modo, recuerda que salvo que una propiedad o método esté declarado como Protegido o Privado cuando se define el método, siempre has de referirte a él desde cualquier parte del programa utilizando MiObjeto.AlgunaPropiedad o MiVentana.AlgunMetodo (e igual con el resto) empleando la notación con punto.

PruébaloDebería de estar preparado un archivo Responses.txt sencillo en el mismo lugar donde accediste a esta lección. Si quieres escribir tu propio archivo, utiliza un editor como NotePad o bien BBEdit; asegúrate de guardar el nuevo archivo como un archivo de Texto.

Eso es todo. Como de costumbre, deberías de probar el programa y trazar aquellas partes en las que estés interesado.

Ejercicios adicionalesObviamente puede mejorarse este proyecto de varias formas. He aquí algunas ideas:

• El proyecto debería mantener un registro mejor de las respuestas. Por el momento, si introduce dos veces lo mismo, el ordenador devolverá en algunas ocasiones la misma respuesta dos veces.

¿Cómo podría solucionarse esto? (una sugerencia: las respuestas no deberían ser aleatorias de entre las posibles coincidencias, sino en orden. Entonces vuelve a reordenar las respuestas de modo que la última se coloque al final de la lista. Puedes mezclar la lista antes de empezar, para conseguir que tenga cierta aletoriedad entre ejecuciones diferentes. También hay otras soluciones, como etiquetar las respuestas de algún modo). Para mezclar una lista, recórrela e intercambia cada elemento con otro seleccionado al azar;

• El proyecto podría empezar solicitando el nombre del usuario, e incorporar a continuación el nombre como parte de la respuesta (digamos, sustituyendo un caracter especial como por ejemplo el asterisco * por el nombre).

• Si te sientes confiado y más ambicioso, permite que las respuestas muestren un cuadro de diálogo y almacenen la respuesta; después permite que se pueda utilizar la respuesta en otras respuestas. El programa tendrá que permitir que el archivo Responses.txt contenga dichas respuestas, y no podrá saber por anticipado cuáles serán sus nombres, de modo que necesitará otro listado para almacenar el nombre de las respuestas. Ahora tendrás dos pares de arreglos con el mismo papel (permitiéndote buscar un valor en el

Page 70: Curso REALbasic

segundo arreglo en función del valor del primer arreglo; el nombre técnico para esta estructura de datos es una tabla de símbolos). Puede que quieras implementar una clase SymbolTable (recuerda las ventajas de los TDA) para que se encargue de realizar ambas tareas.

Page 71: Curso REALbasic

Lección 11 Eventos y Controles

Objetivo

En esta lección continuaremos nuestra exploración de las características de Programación Orientada a Objeto (POO / OOP; Object Oriented Programming) de REALbasic.

Eventos y Controles

En la lección 9 vismos el modo en el que los eventos nos permiten enlazar el código definido en una superclase con el código de su subclase.

Este es el mismo mecanismo utilizado por las clases incorporadas en REALbasic para proporcionarnos manejadores de eventos en los que podemos añadir nuestro código utilizando la ventana correspondiente al Editor de Código.

En esta lección utilizaré los eventos del TextField para crear una nueva versión del EditField que sólo acepte números.

1 Abre un nuevo proyecto.

2 Crea una nueva clase denominada NumEdit, definiendo TextField como su superclase.

3 Abre el Editor de Código para NumEdit y añade el siguiente código en el manejador de evento KeyDown de la clase:

If (Key > "0") and (Key < "9") then Return False Else Return True End if

El argumento Key del manejador del evento representa la tecla que ha sido pulsada. Y el valor de retorno Booleano (True o False; verdadero o falso) del manejador del evento indica a la clase interna de REALbasic qué ha de hacer con la tecla que se ha pulsado: si la función devuelve True, entonces la tecla se ignora; de lo contrario, la pulsación de la tecla se procesa normalmente.

De hecho, las funciones en REALbasic siempre han de devolver algo. Si no devuelves de forma explícita un valor, entonces la función devolverá un valor de “zero” (o, una cadena vacía, False, Nil, etc, dependiendo de cuál sea el tipo de de retorno de la función). Ten en cuenta que cuando no se proporciona un manejador de evento en una subclase, cuando la cuperclase llama a esta función REALbasic devuelve automáticamente el valor por defecto False en la llamada. De modo que, como podrías esperar, si no proporcionas una subclase de TextField con un manejador de evento KeyDown entonces se limitará a procesar cada pulsación de tecla con normalidad.

Page 72: Curso REALbasic

4 Cambia al Editor de Ventana de Window1 y utiliza el menú desplegable situado sobre los controles para camboar el listado de los controles internos a los controles del proyecto.

NumEdit aparecerá listado aquí.

5 Arrastra la clase NumEdit desde el listado de Controles del Proyecto hacia la ventana, y ejecuta el programa.

Advierte que hace prácticamente lo que queríamos, pero que no soporta las teclas de borrado o de cursor. solucionemos eso.

Para ello, necesitamos conocer los códigos ASCII de las teclas que queremos habilitar. Podemos buscarlas en la documentación o en cualquier parte en Internet, pero resulta más sencillo averiguarlo nosotros mismos. Cambia de nuevo al listado de los Controles incorporados y arrastra un TextField estándar sobre la ventana y añade a continuación el siguiente código en su manejador de evento KeyDown:

MsgBox str(Asc(key))

Como de costumbre, puedes buscar Str y Asc en la documentación incorporada.

6 Ejecuta el programa, haz clic sobre nuestro nuevo TextField, y observa los números que aparecen cuando pulsas las teclas de cursor y la tecla de borrado.

7 MOdifica el código del manejador de evento Keydown correspondiente al NumEdit para que sea el siguiente:

If (Key >= "0") and (Key <= "9") then Return False elseif Key >= Chr(28) and Key <= Chr(31) then//tecla de cursor Return False Elseif Key = Chr(8) then //tecla borrar Return False Else Return True End if

8 Ejecuta el programa y comprueba que nuestro nuevo TextField nos permite utilziar ahora las teclas de cursor y la tecla Borrar.

Podemos mejorar aun más la clase permitiendo que el usuario pueda introducir una coma para los valores decimales y un signo de menos para indicar los números negativos. Al hacerlo, sin embargo, debermos conseguir que funcione correctamente y permitir que el usuario introduzca la coma o el signo de negativo sólo una vez y en el lugar apropiado para ello.

Piensa en todo lo que podría estar involucrado para en esto. Lea sobre las propiedades SelStart y SelLength del TextField.

9 Modifica el código para que sea:

Page 73: Curso REALbasic

If (Key >= "0") and (Key <= "9") then Return False Elseif Key = "," and InStr (Text, ",") = 0 then Return False Elseif Key = "-" and SelStart = 0 and Instr (Text, "-") = 0 then Return False Elseif Key >= Chr(28) and Key <= Chr(31) then//tecla de cursor Return False Elseif Key = Chr(8) then //tecla de borrado Return False Else Return True End if

De este modo evitaremos que se pueda utilizar el signo de negación o la coma en más de una ocasión, y sólo dejaremos que se pueda escribir el signo de negación al principio. Ahora el TextField funciona correctamente, permitiendo que el usuario introduzca un número válido pero nada más.

Una subclase adicional

Nuestro siguiente objetivo consiste en crear una subclase d NumEdit que no permita al usuario introducir un número mayor del valor que se haya definido como máximo.

1 Crea una subclase de NumEdit denominada LimitNumEdit.

Para ello puedes acceder al menú contextual utilizando el botón secundario del ratón (o bien Control + clic en el Macintosh) sobre NumEdit en el Editor de Proyecto y seleccionar Nueva subclase en el menú contextual. REALbasic creará una nueva subclase de NumEdit y le pondrá CustomNumEdit como nombre. Utiliza el panel de Propiedades para cambiar su nombre por el de LimitNumEdit.

2 Abre el Editor de Código correspondiente a LimiNumEdit, y advierte que no se encuentra el manejador de evento correspondiente a KeyDown.

Más sobre EventosUna Definición de Evento es una llamada de método que queda conectada con un método real en alguna parte. Al introducir código en un manejador de evento se proporciona el método real al que llamará el evento. Pero este sólo se puede enganchar con un manejador de evento. En una cadena de subclases la “más alta” en proporcionar un manejador de evento se “traga” dicho evento, y no permite que ninguna de las posteriores subclases puedan verlo.

Sin embargo esto no representa un problema, dado que no hay nada que impida que la subclase que gestiona el evento de definir un nuevo evento adicional, que tenga exactamente el mismo nombre y argumentos del que está gestionando, y que llame a éste cuando lo considere oportuno. Veamos cómo podemos hacer esto.

1 Abre NumEdit y añade una nueva definición de evento KeyDown, justo como el original (el mismo argumento y tipo devuelto).

2 Modifica el manejador de evento KeyDown para que sea:

Page 74: Curso REALbasic

If (Key >= "0") and (Key <= "9") then Return KeyDown(Key) Elseif Key = "," and InStr (Text, ",") = 0 then Return KeyDown(Key) Elseif Key = "-" and SelStart = 0 and Instr (Text, "-") = 0 then Return KeyDown(Key) Elseif Key >= Chr(28) and Key <= Chr(31) then//tecla de cursor Return KeyDown(Key) Elseif Key = Chr(8) then Return KeyDown(Key) Else Return True End if

De modo que en aquellas partes donde anterioremente estuviésemos permitiendo una tecla, ahora nos encargamos de pedir a nuestra subclase que decida qué debe hacerse con ella. Si no hay un manejador, entonces se devuelve el valor False por omisión, de modo que el comportamiento sea el mismo que era cuando no existía una subclase, o si dicha subclase no proporcionara un manejador para este evento. Pero si lo hace, entonces puedes restringir aun más la aceptación de las teclas pulsadas.

Sin embargo advierte el modo en el que hemos configurado nuestros eventos y el modo en el que los llamamos no proporciona a la subclase una vía para aceptar más teclas. Si la superclase excluye una tecla, entonces la subclase no llegará a verla o recibirla para tratarla en su propio evento.

3 Comprueba que NumEdit aun funcione.

4 Cambia el TextField de la ventana a un control LimitNumEdit (haciendo clic en el TextField en la ventana y cambiando su Super en el panel de Propiedades), y prueba que aún sigue funcionando.

Dado que no hemos añadido nuevo código, se comporta exactamente del mismo modo que hace su superclase.

Piensa ahora en lo que necesitaríamos para implementar un LimitNumEdit tal y como queremos que sea. Ahora hagámoslo.

5 Añade la propiedad Mximum a la clase LimitNumEdit, así como un méotdo SetMaximum:

Sub SetMaximum(M As Integer) Maximum = M

Por lo general, no deberíamos de definir directamente el valor de una propiedad, de modo que hemos creado este método de asignación para ello.

6 Añade el siguiente código al manejador de evento KeyDown:

Function KeyDown(Key As String) As Boolean Dim ChangedString As String

Page 75: Curso REALbasic

ChangedString = Left(Text, SelStart) + Key + Mid(Text, SelStart + SelLength + 1) If Val(ChangedString) <= Maximum then Return False Else Return True End if

Como de costumbre, consulta cualquiera de las funciones encargadas de tratar el texto, en el caso de que no estés seguro cuál es su cometido.

7 Añade el siguiente código al manejado de evento Open para el control de la ventana:

Me.SetMaximum 500

8 Ejecuta el proyecto, y comprueba que funciona como está previsto.

9 Para propósitos de evaluación, añade una nueva Definición de Evento “Boo” a la clase NumEdit. Crea una nueva clase FurtherNum, utilizando LimitNumEdit como su Super. Advierte que FurtherNum tiene un manejador “Boo”.

Esto es así porque LimitNumEdit no define un manejador de evento para Boo (para que nuestra terminología quede clara en este punto: “no definir un manejador de evento” significa que no hay código en el manejador de evento).

10 Elimina ahora el Nuevo Evento Book de NumEdit y elimina su manejador antes de que guardes la versión final del proyecto.

Es posible que también desees Exportar las clases NumEdit y LimitNumEdit de la Ventana de Proyecto (selecciónalas y busca el comando Exportar en el menú Archivo o en su menú contextual). Estas son de hecho nuestras primeras clases útiles, y puede darse el caso de que las utilices algún día. Así, sólo tendrás que arrastrar los archivos en el Editor de Proyecto correspondiente a otro proyecto, de modo que puedas usarlas de nuevo. Asegúrate de arrastrar NumEdit antes de hacerlo con LimitNumEdit. De lo contrario, no habrá nada de lo que LimitNumEdit pueda ser una subclase cuando se importe, perdiendo por tanto el ajuste de su superclase.

Ejercicios adicionales

Intenta alguno de los siguientes ejercicios:

• Implementa una subclase PositiveNumEdit (para aceptar sólo números positivos);• Utiliza la clase LimitNumEdit en un proyecto con una ventana que realice una operación

sobre los dos números introducidos, mostrando su resultado en un tercer TextField;• Lo mismo pero en este caso utilizando un grupo de botones de radio para seleccionar

las operaciones;• Lo mismo de nuevo, pero actualizando el resultado a medida que se vaya tecleando;• Implementa la subclase RangeEdit, que define un nuevo evento WithinRange, y que es

llamado cuando se introduce un número comprendido en un rango determinado. También necesitarás definir las propiedades y los métodos necesarios para definir el rango.

Page 76: Curso REALbasic

Explicaciones/Referencias:

Los eventos están explicados en el Capítulo 5 de la Guía del Usuario de REALbasic (particularmente en la sección Añadir Nuevos Eventos).

Page 77: Curso REALbasic

Lección 12 Interfaces de Clase

Objetivo

En esta lección examinaremos nuestra última característica principal de POO en REALbasic: la Interfaz de Clase.

La esencial del TipoLa noción de tipo con la que hemos estado trabajando se resume en esto: un tipo es un nombre y un conjunto de operaciones (propiedades y métodos).

Dado que siempre podemos crear métodos de obtención (getter) y definición (setter) para cualquiera de las propiedades que pueda requerir un tipo, la noción más sencilla posible a la que podemos llegar para un tipo es la de un nombre y un conjunto de definiciones de método. (Ten en cuenta que por definiciones de método, no queremos referirnos al código real de los métodos, sino solamente a sus nombres y argumentos4.

Esto es precisamente en lo que consiste una interfaz: un nombre y un conjunto de definiciones de método. Podemos crear un nuevo tipo mediante la creación de una interfaz sobre la que asignaremos definiciones de método. Podemos hacer una clase que tenga dicho tipo añadiendo el nombre del tipo en su propiedad de interfaces, y asegurándonos que soporte todos los métodos definidos por la interfaz.

Advierte que esta idea es diferente de la herencia. La herencia a partir de una superclase común garantiza que dos clases tendrán los mismos métodos y, por tanto, que también podrán ser tratadas como el mismo tipo. Pero la herencia va más allá del mínimo requerido, al proporcionar la implementación de los métodos junto con las propiedades.

Otro modo de describir la diferencia entre una interfaz y una herencia de clase es así: una interfaz define un tipo, pero la herencia también proporciona como mínimo parte de la implementación.

La cuestión obvia ahora es: ¿Por qué no querríamos una clase que tuviese como mínimo parte de la implementación cuando la asignamos a un tipo? (incluso, ¿por qué necesitamos este modo “reducido” de definir un tipo, después de todo?) La respuesta es: nos evita muchas complicaciones cuando queremos que nuestro lenguaje de programación nos permita que nuestras clases tengan más de un tipo.

Múltiples tiposA medida que empiezas a desarrollar programas más complejos, no pasará mucho tiempo en que encuentres normal el hecho de querer que una clase tenga más de un tipo.

El ejemplo más obvio son las clases suministradas: podríamos querer que estas tuviesen papeles adicionales, y el modo natural de conseguirlo es proporcionándoles tipos polimórficos adicionales. En el programa que crearemos en esta lección, ampliaremos el proyecto de contar palabras de modo que pueda mostrar el resultado tanto en un ListBox como en un TextField.

4 Otro nombre que oirás para esto es el de una firma de método, declaración de método o signatura de método.

Page 78: Curso REALbasic

El modo correcto de lograr esto en un lenguaje de programación orientado a objeto como REALbasic consiste en proporcionar el mismo tipo a estas dos clases, de modo que podamos asignarles las operaciones adecuadas para que muestren el contador de palabras.

Pero REALbasic sólo nos permite asignar una superclase a una clase. La pregunta más obvia ahoa es: ¿Por qué no puede una clase tener múltiples superclases?

Sólo una Superclase

Hay buenas razones para permitir sólo una superclase para una clase dada. Los lenguajes de programación que nos permiten tener más de una superclase entran en una variedad de complicaciones. Por ejemplo, ¿qué ocurre si las dos superclases tienen un método o propiedad con el mismo nombre? ¿Qué ocurre cuando las dos superclases tienen una superclase en común?

Figura 44. Una jerarquía de clase hipotética.

(Captura Figura 44)

En un lenguaje de programación que soporte una herencia múltiple como esta, asume que Class1 tiene una propiedad Size. Esto significa que tanto Class2 como Class3 tienen una propiedad Size. ¿Debería Class4 tener sólo una propiedad Size, o dos (presumiblemente, tendríamos algún modo de cambiar el nombre de una de ellas)? La respuesta es que depende: puede que quieras sólo una propiedad, pero es posible que también puedas querer las dos. Hay otras complicaciones, como qué ocurriría con un método de la clase Class1 que fuese modificado en Class2 pero no en Class3.

Consideraciones como esta significan que los lenguajes de programación que soportan la herencia múltiples son convulsos y dificultan por lo general los mecanismos de mantenimiento para controlar el modo en el que se comporta y realiza el proceso de herencia.

Las interfaces evitan este problema, dado que no incorporan implementación.

Un Contador de Palabras muy mejorado

1 Abre el proyecto de la Lección 9 (el contador de palabras que puede contar tanto a partir de archivos como desde el portapapeles).

2 Añade un TabPanel a Window1 (lee sobre el uso de los TabPanel en la Guía del Usuario).

3 Usa dos paneles con los nombres “List” y “Text” y pon el ListBox WordList sobre la primera página del TabPanel, de modo que la ventana tenga este aspecto:

FIgura 45. Window1, mostrando el ListBox.(Captura Figura 45)

4 Añade un TextArea con el mombre WordLister a la segunda págna del TabPanel.

Page 79: Curso REALbasic

5 Activa las propiedades MultiLine y ScrollBarVertical, de modo que esto sea lo que veas cuando cambies a la segunda página del TabPanel:

Figura 46. Window1, mostrando el TextArea.

(Captura Figura 46)

Examina los métodos y propiedades definidas en Window1 y considera por qué, teniendo en cuenta lo aprendido en anteriores lecciones, sería más apropiado que figurasen en clases separadas.

Piensa sobre la forma que podría tener dicha clase (necesitará todos los métodos y propiedades definidas en Window1, para empezar...).

6 Crea una clase WordCountList, y mueve los métodos y propiedades de Window1 a la nueva clase.

7 Añade una propiedad WordCounter As WordCountList a Window1.

8 Añade un manejador de evento Open a la ventana, con código para inicializar WordCounter:

WordCounter = New WordCountList

9 Abre el código en cualquiera de los botones, y piensa sobre cómo deberías modificarlo para acomodarlo al nuevo objeto.

Debería resultar sencillo cambiar las partes superiores del código, pero surgirá un problema interesante cuando consideres el modo de mostrar los resultados. Puede que te sientas tentado de añadir métodos Get a la clase WordCountList, de modo que puedas iterar la lista de palabras y mostrar sus contenidos, tal y como veníamos haciendo hasta ahora.

Hacerlo sería un diseño pobre.

Proporciona a los Objetos un comportamiento de nivel superior (o “piensa dos veces sobre los Métodos Accesorios”)

Esta parte es muy importante. Es un ejemplo del tipo de cosas que necesitas aprender para que seas un buen programador orientado a objetos.

Debes pensar dos veces sobre cualquier diseño en el que se vean envueltos métodos Get y Set (también conocidos como Métodos Accesorios o Accessor Methods). Si bien resultan apropiados por lo general, deberías ver si existe una solución de diseño mejor. Cualquiera de dichas soluciones involucra por lo general proporcionar métodos de un nivel superior al objeto en el que estás tentado de añadir métodos accesorios. Por “nivel superior”, queremos indicar algo así como ʻmás sofisticadoʼ o ʻrealizando una tarea más ampliaʼ.

El diseño actual es un ejemplo excelente de esto: el trabajo de mostrar el contador de palabras podría estar gestionado entre la clase WordCountClass que tendrá el listado de las palabras, y la clase que será la encargada de mostrar dicho listado.

Page 80: Curso REALbasic

Una versión ligeramente distinta de este diseño heurístico (que significa ʻregla de oroʼ) consiste en decir que el código encargado de realizar la tarea debería encontrarse distribuido entre los objetos que deben encontrarse involucrados. Esta heurística nos indica que el botón y la ventana no deberían de contener ninguna parte de la lógica relacionada con mostrar el contador. Esta regla permite mantener nuestro código más simple, dado que nos proporciona menos lugares en los que debamos mirar en busca de posibles problemas, al tiempo que hace que los objetos sean más portables entre diferentes proyectos (o entre diferentes partes de un mismo proyecto).

1 Ahora que hemos tomado nuestra decisión sobre el diseño, este es el código para el manejador de evento Action del botón correspondiente al Clipboard:

//Ejecuta CountWords en el portapapeles, y copia a continuación los resultados en WordListDim Source As ClipBoardSource Source = New ClipboardSource WordCounter.CountWords(Source) WordList.clear WordCounter.Display WordList WordLister.clear WordCounter.Display WordLister

Otro buen diseño habría consistido en que el método Display ejecutase el mètodo clear; sin embargo, al separar dicha llamada, matenemos algo de flexibilidad extra para, por ejemplo, mostrar el contador de palabras en varias etapas o a partir de varias fuentes.

2 Selecciona Proyecto > Añadir > Interfaz de clase5. Nómbralo WordCountDisplayer en el panel Propiedades.

3 Haz doble clic en la interfaz de clase en el Editor de Proyecto, y añade dos definiciones de método:

Sub Clear()

y

Sub Add(w As String, c As Integer)

4 Crea un método con el nombre “Display” en la clase WordCountList:

Sub Display(w As WordCountDisplayer) Dim counter As Integer w.Clear For counter = 0 to UBound(Word) w.Add Word(counter), Count(counter) Next

5 Crea una clase WordCountListBox, con ListBox como super.

5 También puedes acceder al menú contextual o hacer clic sobre la barra de herramientas de la pestaña Proyecto; selecciona Personalizar... y añade Interfaz de Clase a la barra de herramientas.

Page 81: Curso REALbasic

6 Haz clic en la propiedad Interfaces e introduce WordCountDisplayer. Implementa los métodos Add y Clear correspondientes:

Sub Add(w As String, c As Integer) AddRow str(c) Cell(ListCount - 1, 1) = w

y

Sub Clear() DeleteAllRows

7 Cambia el código en el manejador de evento Action correspondiente a CountFileButton:

//Solicita un archivo al usuario, y ejecuta a continuación CountWords sobre dicho archivo,//copiando los resultados en WordList Dim counter As integer Dim Source As FileSource Dim f As FolderItem Dim s As BinaryStream Source = new FileSource //Solicita el archivo f=GetOpenFolderItem("application/text") If f<>Nil then s = f.OpenAsBinaryFile(False) Source.SetFile s WordCounter.CountWords(Source) WordList.clear WordCounter.Display WordList End if

8 Utiliza el panel de Propiedades para cambiar el ListBox a WordCountListBox.

Antes de que procedas con las siguientes instrucciones, prueba a crear WordCountEditField por ti mismo. Tendrás que decidir cómo mostrar los resultados (quizá una palabra en cada línea, teniendo en cuenta que cada línea se lea algo así como “manzanas: 3”).

Si necesitas ayuda, este es un modo de crear dichos métodos para el WordCountEditField:

Sub Add (w As String, c As Integer) If Text = "" then Text = Text + w + ": " + Str(c) Else Text =Text + Chr(13) + w + ": " + Str(c) End if

Sub Clear() Text = ""

Page 82: Curso REALbasic

Es posible que tu código sea distinto a este, y también puede que muestre los resultados de un modo algo distinto. Está bien, isempre y cuando funcione.

No te olvides de añadir WordCountDisplayer a la propiedad de Interfaces para la clase, y de cambiar el EditField de la segunda página del TabPanel en Window1 para esta nueva clase.

9 Ejecuta el programa, y avanza a través de las partes importantes en el caso de que no tengas claro cómo funciona.

Preguntas y Conclusiones

Las interfaces de clase son una herramienta importante para producir los mejores diseños de programa en REALbasic. Ahora tienes todas las herramientas que necesitas para simplificar y estructurar tu código con eficiencia utilizando el Polimorfismo, siempre que sea apropiado.

Ejercicios adicionales

Prueba a realizar alguno de los siguientes ejercicios:

• Añade una nueva clase WordCountDisplayerGroup al proyecto. Su tarea consiste en almacenar referencias a múltiples WordCountDisplayers y “emitir” llamadas a cada uno de los objetos a los que se refiera. De modo que también es un WordCountDisplayer, pero también tiene un método Add, con un argumento WordCountDisplayer. Los botones sólo necesitan indicar al contador de palabras que se muestre a sí mismo en el WordCountDisplayerGroup, y dicho objeto se encargará de pedir a cada uno de sus visualizadores que se encarguen de mostrar los contenidos.

• Añade la capacidad de “mostrar” un archivo delimitado por tabuladores. Esto debería funcionar correctamente con una tercera “pestaña” que te permitiese seleccionar el archivo de destino. Para escribirlo, necesitas investigar sobre cómo escribir un archivo.

Comentario Final

Ten en cuenta que la Guía del Usuario indica que las interfaces sólo están para usarlas con los controles, pero esto no es así: también son indicadas para otras situaciones con cualquier tipo de objeto.

Deberes: Escribir a Archivo

Tu tarea consiste en añadir un nuevo WordCountDisplayer al proyecto contador de palabras en el que estamos trabajando. Esta nueva clase escribirá el contador de palabras a un archivo.

Como mínimo tendrás que leer la sección de la Guía del Usuario de REALbasic correspondiente a Obtener un Archivo en una Ubicación Específica. También deberías de leer Comprender los FolderItems y Trabajar con Archivos de Texto.

El archivo de texto debería de encontrarse delimitado con rabuladores, lo que significa que cada palabra y su contador está escrito al archivo como la palabra seguida por un

Page 83: Curso REALbasic

tabulador, y seguida por el contador (como cadena), y seguida por último con el caracter de retorno (ASCII 13).

Deberías de escribir en primer lugar una clase que se encargase de enviar la información al archivo con un nombre determinado en el escritorio. Busca la función DesktopFolder en la referencia incorporada para obtener más información y ejemplos sobre cómo utilizar esta función.

Una vez que esté funcionando, deberías modificarla de modo que pueda proporcionársele una ruta o un folderitem (a tu elección) sobre el cual guardar la información. Deberías añadir un panel al TabPanel con un botón que permita al usuario crear el archivo que contendrá la información. Deberías mostrar la ruta seleccionada en un EditField en el control de pestaña. Quizá quieras añadir un checkbox para activar o desactivar dicha exportación.

Echa un vistazo a GetSaveFolderItem para comprobar cómo puedes mostrar el cuadro de diálogo estándar correspondiente a guardar archivo. También deberías echar un vistazo a la clase FolderItem para averiguar como puedes encontrar información como la ruta del FolderItem una vez que el usuario haya creado el archivo.

Page 84: Curso REALbasic

Lección 13 Ordenación por el método de la Burbuja

Objetivo

Tomaremos un descanso de la programación orientada a objetos para volver a los algoritmos. Durante las próximas lecciones examinaremos una serie de diferentes modos de ordenar una lista.

Una vez examinado dicho código y técnicas habrás logrado lo siguiente:

• Empezarás a aprender la rica y compleja noción de eficiencia en la programaciónd e ordenadores;

• Verás un ejemplo sobre la cantidad de modos que suelen encontrarse para solucionar un problema dado (incluso si aparentemente parece que sólo haya uno) con un ordenador, y

• Verás algo más de código. Es importante leer una buena cantidad de código.

Sobre la eficiencia

Existen unas cuantas nociones específicas de eficiencia en ciencias de la computación, y las veremos en un momento. Pero antes de que lo hagamos, considermos la eficiencia de un modo más amplio.

Tiempo del Programador frente a Tiempo del Usuario

Comenzaremos observando que existens dos tiempos6 diferentes utilizados por un programa de ordenador:

• El tiempo del programador, utilizado en escribir el programa; y• El tiempo del usuario, esperando a que el programa termine.

El tiempo del programador es escaso

A continuación, consideremos que existen una serie de tareas que consumen el tiempo del programador:

• Hacer que un programa funcione correctamente;• Hacer que un programa funcione más rápido (o con menos memoria);• Hacer que un programa se pueda cambiar con facilidad;• Haciendo alguna otra cosa (escribiendo otro programa diferente; dar un paseo al perro;

aprendiendo español...)

(Las tres primeras no están claramente separadas. Por ejemplo, hacer que el programa sea más fácil de cambiar y hacer que sea correcto parece que vayan de la mano).

6 En realidad, hay potencialmente otros tipos de tiempo, y otros recursos escasos que probablemente queramos perservar: tiempo de transmisión en red, o tiempo para mostrar algo, o bien tiempo para mostrar el primer resultado derivado de una tarea compleja.

Page 85: Curso REALbasic

Nuestra segunda observación, entonces es que, existen varias demandas sobre el tiempo del programador.

En este punto también advertimos que la mayoría de los programas sólo se utilizarán durante un periodo de tiempo determinado, después del cual se utilizará una nueva versión, o bien el programa dejará de utilizarse del todo.

El tiempo del usuario, frente al tiempo del ordenador

El tiempo del ordenador está siendo cada vez más barato y a mayor velocidad. Mientras tanto, las personas trabajan a la misma velocidad a la que siempre lo han hecho.

Cada vez con mayor frecuencia, cuando un usuario ejecuta un programa prácticamente todo el tiempo involucrado en el proceso consiste en que el usuario defina los datos, seleccione qué programa ejecutar, leer los resultados, etc.

Por qué deberías escribir probablemente muchos programas sucios y rápidos

Todo esto nos lleva a la conclusión de que una buen cantidad de programas deberían de ser escritos utilizando el código más sencillo y rápido posible.

Tu tiempo probablemente sea más escaso que el de tus usuarios. Incluso esté bien si no se trata de un programa “lento” que termine en un segundo (quizá tu programa sea usado por un millón de personas), en comparación con un programa “rápido” que termina en 1/100 de segundo. Escribiendo programas sencillos existen multitud de tareas sencillas que puedes evitar a las personas. Si haces muchas de estas, entonces la escritura de programas sucios y rápidos es una cuestión a tener en cuenta.

Cuando deberías invertir más tiempo escribiendo un programa

Deberías pasar más tiempo escribiendo un programa en los siguientes casos:

• El programa no funcionará en “un instante” si no prestas atención a la velocidad del programa, y se utilizará con frecuencia o por muchas personas;

• El programa debe de encargarse de un problema “de gran tamaño” (digamos echando una hora adicional escribiendo código más eficiente se traduce en que el programa termina en unos pocos minutos en vez de en una semana; y como veremos este tipo de diferencia es sorprendentemente común);

• El programa será usado durante mucho tiempo. Probablemente se modifique repetidamente, de modo que deberías invertir como mínimo el tiempo necesario para realizar su diseño y hacer que su modificación resulte sencilla, además de encontrarse bien documentado;

• Cuando la velocidad del código se manifieste a sí misma como otro factor de calidad percibido por las personas, entonces también querrás optimizar la velocidad de tu código. El ejemplo más común de esto se encuentra en los juegos: cuanto más rápido se ejecute tu código, más imágenes podrá generar por segundo, lo que significará que la animación será más suave.

Probablemente puedas concebir otra serie de criterios además de los expuestos.

Page 86: Curso REALbasic

No desperdicies tiempo en “eficiencia” innecesaria

Invertiremos algo de tiempo echando un vistazo a la noción de eficiencia en programación tal y como la definen los propios programadores por lo general. A medida que lo hagamos, verás que con bastante frecuencia el código que es más fácil de escribir también correrá con mayor lentitud (en algunos casos con mucha más lentitud).

No cometas el error en el que incurren de forma sorprendente muchos programadores, pensando que el código más lento y sencillo es malo. Con bastante frecuencia, se trata de un código mejor, dado que lo escribes más rápido y resulta más sencillo de comprender.

Las diferentes partes de un programa requieren de diferentes dosis de atención

Una observación final: incluso en un programa en el que decides invertir una buena cantidad de tiempo optimizándolo (lo que significa que consigues que funcione más rápido en menos memoria), habrá partes del programa donde no puedas hacer más. Sólo podrás optimizar las partes del programa en las que el ordenador emplee una gran cantidad de tiempo.

Cuatro lecciones sobre Ordenar

Ahora veamos la más noción más breve sobre la eficiencia.

Uno de los libros más famosos sobre programación, The Art of Computer Programming, describe 29 modos diferentes de ordenar un listado de cosas (junto con algunas variaciones menores). Y el listado en dicho libro no es exhaustivo.

En esta y las siguientes tres lecciones, veremos cuatro de estos modos de ordenación. Examinaremos cómo funcionan, probaremos lo rápidos que son; consideraremos brevemente otros problemas, como por ejemplo cuál sería el peor y mejor caso de rendimiento; y consideraríamos variaciones menores sobre ellos con el objeto de mejorar su rendimiento.

Ordenación por el método de la burbuja

Comenzaremos con el método de la burbuja, probablemente el que suponga el algoritmo de ordenación más sencillo, y también uno sorprendentemente ineficiente.

1 Abre el proyecto BubbleSort y examina el código del manejador de Evento SortEvent correspondiente a la clase BubbleSortList.

2 Pon un punto de parada en el comando Do.

No hemos visto anteriormente el Do... Loop Until. Es justo como el bucle While... Wend, exceptuando que ejecuta el cuerpo del bucle una vez antes de probar la condición, y entonces deja de ejecutar el bucle cuando la condición se evalúa a True y no como False.

Deberías probar a imaginar cómo se realiza la ordenación examinando para ello el código, antes de proceder a leer la explicación en el siguiente párrafo.

Cómo funciona la ordenación por el método de la burbuja

Page 87: Curso REALbasic

El método de la burbuja funciona pasando por la lista de un extremo hasta el otro, examinando cada par de elementos adyacentes en la lista e intercambiándolos en el caso de que no estén ordenados. Este procedimiento se repite hasta que se haya pasado de un extremo a otro de la lista sin haber intercambiado ninguno de sus elementos.

Probablemente nunca se te haya ocurrido ordenar una lista de este modo, y es obvio lo ineficiente que resulta. Pero el código para implementar un Método de Burbuja es muy simple. No tengas miedo de implementar un Método de Burbuja si necesitas ordenar una pequeña cantidad de información en un programa rápido y sucio. Por otra parte, en la siguiente lección, veremos un algoritmo mucho más eficiente que es casi igual de sencillo de implementar (ordenación por inserción).

1 Echa un vistazo al código en el manejador de evento Acción correspondiente al botón Go. Ejecuta el programa y haz clic sobre el botón Go. Traza la ejecución del código examinando los contenidos del arreglo a medida que avances.

Este programa se ha mantenido muy simple de forma deliberada. Se espera que ejecutes el programa en el depurador para que veas lo que hace, y si quieres ordenar una cantidad distinta de elementos, entonces tendrás que cambiar el código en el botón. Todo esto se hace para mantener nuestra noción de eficiencia. Algunas veces es importante probar el código para comprobar su eficiencia, así como para comprobar su funcionamiento, pero si no pretendemos crear un programa final o bien estás intentando ejecutar una serie de pruebas, entonces no merece la pena invertir mcuho tiempo haciendo que el test sea vistoso. Veremos más sobre esto en las siguientes lecciones.

Asegúrate de que examinas el código y ves como hemos usado el polimorfismo para separar la implementación de la ordenación frente al código que solicita la ordenación. El código que solicita la ordenación sólo necesita saber que la cosa a ordenar es un NumList. Esto será importante en futuras lecciones, donde ejecutaremos diferentes algoritmos de ordenación y los compararemos.

La Definición de Eficiencia más Breve

Dejando de lado toda nuestra discusión sobre la importancia de tu tiempo, es muy importante en ciencia de la computación comprender el modo en el que vemos la eficiencia. Hay varias situaciones donde será importante escribir código eficiente.

Existen varios tipos de eficiencia, pero las dos más importantes son: ¿cuanto tiempo necesita un algoritmo para finalizar? y, ¿cuánta memoria necesita un algoritmo?

El Método de la Burbuja es muy bueno desde el punto de vista de su eficiencia con la memoria: más allá de la propia lista de elementos, necesitamos un contador, una variable que se usará en el intercambio y un Booleano. Incluso se puede prescindir del contador (piensa sobre el modo de escribir el método sin que sea preciso uno).

Pero el código es muy ineficaz desde el punto de vista del tiempo. Prueba a aumentar la lista a 1.000 elementos y comprobarás la cantidad de tiempo que requiere para finalizar. Cuando el tiempo para completar un algoritmo aumenta muy rápidamente con el tamaño del problema (en este caso, el ʻtamaño del problemaʼ sería la cantidad de elementos en la lista), decimos que el algoritmo es ineficaz en el tiempo.

Page 88: Curso REALbasic

Casos Mejor y Peor

El mejor caso para este algoritmo es una lista ya ordenada. Bajo este supuesto, lo completará con la rapidez necesaria para comprobar que la lista ya está ordenada. Y una lista casi ordenada también terminará muy rápido también7.

El peor caso para el Método de la Burbuja es cuando la lista está ordenada en orden inverso. En dicho caso, cada vez que se itere la lista se ordenará en su lugar uno de los elementos, y el resto de la lista permanecerá sin cambios. En una lista de n elementos, un método de la bubuja en el peor de los casos tendrá que hacer n2 comparaciones (dado que busca una lista de n elementos n veces).

Ejercicios adicionales

A medida que observes el algoritmo en acción, y si piensas en lo que hace, observarás que la primera vez que se ejecuta el bucle Do el elemento más grande de la lista se pasa a la última posición; la siguiente iteración sobre la lista, el segundo elemento más grande se pasa a la penúltima posición, etc.

• Como resultado de dicha observación, considera cómo podría mejorarse el algoritmo.

El algoritmo mejorado debería precisar sólo n2-n comparaciones para una lista de n elementos.

Una mejora razonablemenete sencilla del algoritmo consiste en crear pasadas alternativas a lo largo del listado en la dirección opuesta, con ello se mejora ligeramente el tiempo medio, y también mejora de forma palpable el peor de los casos. Resulta difícil llegar a una expresión matemática simple sobre cuántas comparaciones se requieren en el peor caso, pero es considerablemente mejor que cualquiera de los anteriores.

7 Puede que existan ocasiones en las que escribirás un programa que deba tratar con esta situación: necesitas ordenar una lista que está prácticamente ordenada. En este caso, el método de la burbuja (o más probablemente una ordenación por inserción) serán más rápidos que otros métodos que veremos posteriormente.

Page 89: Curso REALbasic

Lección 14 Ordenación por Inserción

Objetivo

En esta lección veremos otro algoritmo de ordenación. También crearemos un programa que nos permitirá comparar con facilidad diferentes algoritmos de ordenación.

La ordenación por inserción

Comencemos considerando cómo ordenarías una pila de tarjetas que contuviesen un nombre sobre ellas.

Probablemente pienses en utilizar una ordenación por inserción: esencialmente, empezando con la primera tarjeta y creando una nueva pila de tarjetas donde cada una de las tarjetas ocuparía la posición correcta. A medida que cojes una tarjeta de la pila no ordenada encontrarías su lugar en la nueva pila ordenada y la insertarías en ella. Esto es exactamente lo que queremos hacer en el ordenador.

Piensa sobre cómo implementarías esto en el ordenador. Probablemente quieras mover los elementos de un arreglo en otro arreglo, imitando lo que harías con las tarjetas reales.

Ahora pregúntate si es posible implementar el mismo algoritmo utilizando sólo un arreglo. Observa que no cambia la cantidad total de elementos involucrados en la lista ordenada y en la lista desordenada: a medida que una lista aumenta, la otra se reduce en una cantidad idéntica. A partir de este punto, podemos deducir que es posible ordenar el arreglo en sí mismo, creando para ello un arreglo ordenado a partir de uno de los extremos del arreglo desordenado. Esto ahorrará tiempo y memoria (ahorramos tiempo porque el ordenador no precisa alojar un nuevo arreglo), sin tener que hacer que el algoritmo sea significativamente más complejo.

A medida que lo implementos verás que mover un elemento n a su posición correcta involucrará rotar hacia bajo en uno la lista de elementos entre su nueva posición y la antigua posición; siendo el propio elemento ordenado el que se sitúe sobre la sección rotada del arreglo.

De modo que parte del algoritmo será una operación rotar sub-arreglo.

1 Abre el proyecto InsertionSort.

2 Echa un vistazo rápido al proyecto

Nos permite ejecutar más de un algoritmo de ordenación sobre la misma información y comparar el tiempo consumido por cada uno de los múltiples algoritmos.

3 Abre la clase InsertionSortList. Examina el método Rotate.

Bastante evidente.

4 Ahora examina el manejador de evento SortEvent.

Page 90: Curso REALbasic

Asegúrate de que también compruebas la estructura general del proyecto, y observa cómo hemos usado las características de la programación orientada a objetos para simplificar el diseño.

5 Ejecuta el proyecto y prueba con un ranto de valores interesante: 100 a 1.000 en pasos de 100 probablemente suponga un buen ajuste.

Examina los resultados

El siguiente paso es opcional, pero si te sientes cómodo con una hoja de cálculo, entonces supone un buen modo de comparar los resultados.

Una vez que se haya ejecutado el programa, este mostrará un TextArea con los tiempos obtenidos en cada uno de los dos algoritmos de ordenación. Copia los valores desde el TextArea y pégalos en la hoja de cálculo. Ordena la hoja de cálculo por la primera columna, y corta a continuación los valores de la columna de la derecha en la segunda mitad de las filas a lo largo de la columna de la derecha y en la primera mitad de filas, tal y como se ve aquí (los círculos muestran a partir de qué punto se han pegado los datos):

Bubble Sort Array 100 24 11

Bubble Sort Array 200 89 54

Bubble Sort Array 300 223 104

Bubble Sort Array 400 469 208

Bubble Sort Array 500 617 323

Bubble Sort Array 600 880 413

Bubble Sort Array 700 1060 641

Bubble Sort Array 800 1398 771

Bubble Sort Array 900 1766 981

Bubble Sort Array 1000 2732 1248

Insertion Sort Array 100 11

Insertion Sort Array 200 54

Insertion Sort Array 300 104

Insertion Sort Array 400 208

Insertion Sort Array 500 323

Insertion Sort Array 600 413

Insertion Sort Array 700 641

Insertion Sort Array 800 771

Page 91: Curso REALbasic

Insertion Sort Array 900 981

Insertion Sort Array 1000 1248

Ahora podrías crear una gráfica a partir de las tres columnas de la derecha sobre la parte superior de las filas, utilizando para ellos las tres primeras columnas como etiquetas, viendo de este modo el comportamiento de ambos algoritmos. Deberías de poder crear una hoja de cálculo parecida a esta:

Figura 47. Una hoja de cálculo y una gráfica del resultado.

(Captura Figura 47)

El nuevo algoritmo es claramente mucho más rápido; pero ten en cuenta que las dos curvas tienen el midmo tipo de forma (parabólica). Esto significa que el nuevo algoritmo obtiene unos tiempos similares apra los conjuntos de datos que no son mucho mayores. Ambos algoritmos requieren de una buena cantidad de tiempo con conjuntos de datos de gran tamaño.

Pregúntate en qué punto merece la pena el tiempo invertido por la ordenación por inserción. La respuesta: rotando los elementos en la lista.

Ejercicios adicionales

En este momento sería interesante que te preguntases a ti mismo de qué modo podría mejorarse este algoritmo. Existen varias posibles respuestas a esta pregunta...

Esta es sólo una de ellas para ayudarte a empezar, y que deberías intentar implementar: es ineficiente buscar el punto de inserción simplemente empezando por la parte superior de la lista y observando uno por uno a lo largo de la lista, dado que la lista ya está ordenada. En vez de ello, implementando una búsqueda binaria, empiezas buscando en la mitad de una lista que ya está ordenada, saltando a continuación a la mitad de la primera o segunda mitad, y a continuación a mitad de la primera o segunda mitad y así sucesivamente, hasta que hayas delimitado la ubicación que estás buscando en quizá un máximo de cinco elementos, y cambiando a continuación a la búsqueda lineal de uno a uno. Esto mejorará tu tiempo de ordenación notablemente.

Page 92: Curso REALbasic

Lección 15 Redefinir la Ordenación por Inserción

Objetivo

En esta lección veremos una variación de la ordenación por inserción. Crearemos un framework que nos permite comparar la velocidad de diferentes algoritmos de ordenación, y lo utilizaremos para ver si la nueva variación es más rápida.

Sobre las Pruebas

Con frecuencia resulta útil realizar pruebas para comparar la velocidad de los algoritmos. En muchos casos no resulta fácil pobar si uno de ellos es más rápido que el otro o bien cuándo es más rápido.

Un lenguaje de alto nivel como REALbasic introduce otra complicación: REALbasic hace varias cosas por nosotros ʻentre bambalinasʼ, y no sabemos exactamente cómo hace dichas cosas, o cuándo las hace. Por ejemplo, a medida que creamos y destruimos las variables o bien cambiamos el tamaño de los arreglos o las cadenas, REALbasic está realizando por nosotros las cuestiones relacionadas con la gestión de memoria (siguiendo las variables usadas en cada sitio, el tamaño de cada una de ellas, y reclamando el espacio ocupado por las variables destruidas o cuyo tamaño ha sido modificado). En algunas ocasiones, la actividad que REALbasic está haciendo entre bambalinas introducirá algunas incertidumbres o impredicción en el tiempo requerido por el algoritmo o bien una parte de este para su finalización.

Como verás en esta lección, sólo porque sintamos que algo debe ser más rápido no significa que realmente vaya a ser así...

Varias formas de mejorar la Ordenación por Inserción

Al final de la última lección, te pedimos que pensaras en formas de mejorar el algoritmo correspondiente a la ordenación por inserción. Existen varias posibles respuestas para esto. Estas son unas cuantas:

• Asumiendo que los valores a ordenar estén distribuidos de forma aleatoria, empieza buscando por un punto de inserción cuyo valor de índice esté estimado a partir del valor que se insertará (de modo que si hay 100 elementos en la lista, con valores hasta 100, comienza buscando el punto en el que insertar el valor de 50 en la mitad de los valores ordenados más alejados, y empieza a buscar sobre dónde insertar el valor 25 dentro del primer cuarto del listado);

• Realiza una búsqueda binaria para el punto de inserción (busca en el valor medio, y a continuación la mitad d ela lista por encima o por debajo de dicho punto, y a continuación en la mitad de los valores restantes, etc).

• Ordena en N listas independientes, cada una de las cuales se encarga de un rango concreto de valores, y mezcla a continuación dichas listas al finalizar; y

• En vez de rotar los elementos en el arreglo donde se insertará el valor, mantiene una lista de los siguientes valores del índice en un arreglo paralelo, y averigua el orden correcto empleando dicho arreglo. Esto hace que la inserción utilice un número fijo de

Page 93: Curso REALbasic

operaciones, en vez de que requiera una cantidad de tiempo mayor a medida que la lista también se hace más grande.

Por supuesto también es posible combinar algunas de estas dieas. Probaremos la última idea. Parece que fuese la más rápida, dado que la asignación de un par de elementos de arreglo debería de resultar considerablemente más rápido que la mayor cantidad de asignaciones involucradas en la operación de rotación sobre un listado de gran tamaño.

1 Abre el proyecto 15_InsertionSort2. Ejecútalo para un ranto de 100 a 1.000 en pasos de 100.

Es probable que quieras copiar los resultados en una hoja de cálculo y crear una gráfica de estos.

Es probable que encuentres que este nuevo algoritmo no es más rápido (fue más rápido para 900 elementos o más en un PowerBook G3/333 bajo Mac OS 9, pero más lento para cualquier cantidad de elementos en el mismo ordenador bajo Mac OS X). Parece razonable esperar que el nuevo algoritmo será más rápido, de modo que este resultado es interesante (¡y demuestra por qué deberíamos comprobar nuestras ideas preconcebidas).

Después de la operación de ordenación, un elemento del arreglo enlazado contiene el valor del índice del elemento en el arreglo Numbers y que va a continuación del elemento del arreglo de valores en el mismo índice. Esto resulta más sencillo de ver que de explicar, de modo que he aquí un ejemplo:

Índice Numbers Arreglo Link

1

2

3

4

5

6

7

5 2

5 -1

1 6

3 5

4 7

2 4

4 1

El arreglo link nos indica qué fila viene a continuación. Mantenemos un seguimiento por separado del elemento más bajo que debe ordenarse en el arreglo, de modo que ahora sabemos que la fila 3 va en primer lugar (contiene el valor más bajo, de 1). Miraremos a continuación en el arreglo Link en la fila 3, y esta nos indica que la fila 6 es la siguiente (dado que tiene el siguiente valor más bajo, de 2). Esta va seguida por la fila 4, y así con el resto. La última dila, número 2, tiene un valor para el arreglo Link de -1, para indicarnos que no hay un elemento siguiente.

Para insertar una fila en la lista, es una simple cuestión de actualizar los valores enlazados de los elementos que le preceden y suceden en oden. Parece razonable

Page 94: Curso REALbasic

esperar eso para los arreglos de gran tamaño, debería resultar más rápido saltar simplemente a un elemento del arreglo de enlace, en vez de tener que rotar una gran parte del listado.

Incluso si este método es más rápido, la simple construcción del arreglo de enlace no es lo mismo que ordenar el arreglo original. Buscar en el arreglo de una forma ordenada será más lento si es necesario seguir el arreglo enlazado hasta encontrar el siguiente elemento. Y encontrar el enésimo elemento en orden será muy lento. Esta es una situación común: el algoritmo más adecuado depende en muchas ocasiones de cómo queramos utilizar los resultados.

2 Abre la clase LinkInsertionSortList, y examina el manejado de evento SortEvent.

Como ejemplo, aquí estamos haciendo todo en un método de gran tamaño dado que hacer todo mediante la llamada a un méotdo en vez de en la posición actual resulta un poco más lento. Esto puede resultar de importancia cuando mulitplicas esa pequeña diferencia por una gran cantidad de repeticiones a lo largo del bucle.

Recuerda nuestras discusiones sobre la eficiencia. Nos interesaría hacer esto si redundase en un código notablemente más rápido; el retardo singificara que el alguien debiese esperar, y no tuviésemos algo más importante en lo que invertir nuestro tiempo de programación.

Comentar y descomentar el Código

Hay tres modos de indicar que algo escrito en REALbasic es un comentario. Dos de ellos consisten en comenzar una línea con // y comenzar la línea con ʻ. Deberías usar siempre // para los comentarios reales, dado que existe una diferencia en el uso del estilo de comentario ʻ: REALbasic proporciona un botón Comentar/Descomentar en la barra de herramientas del Editor de Código. Este añadirá o eliminará la marca de comentario ʻ de un bloque de líneas. Puedes utilizarlo para desactivar o activar temporalmente una porción de código, lo que puede resultar de gran utilidad durante las tareas de depuración. Si siempre utilizas las dobles barras inclinadas prara tus comentarios, entonces los comandos incorporados de Comentar/Descomentar no interferirán con tus comentarios reales.

El manejador de evento LinkInsertionSortList tiene parte del código comentado de esta forma hacia el final. Este utiliza el arreglo de enlace para ordenar el arreglo (de modo que no se ralentice el acceso a un elemento concreto del arreglo ordenado). Si obtuviste resultados más rápidos para el nuevo algoritmo, prueba de nuevo eliminando previamente los comentarios. El nuevo algoritmo debería de resultar ahora más rápido, si bien el umbral sobre el que se apreciará su mayor velocidad se situa en una cantidad mucho mayor para la lista.

¿Por qué es más lento el nuevo algoritmo?

Resulta difícil estar seguro por qué el nuevo algoritmo no puede ser más rápido, pero podemos hacer algunas deducciones.

En primer lugar, el nuevo algoritmo ha de hacer más trabajo para empezar. El nuevo algoritmo debe de modificar el tamaño de dos arreglos en vez de simplemente uno.

Page 95: Curso REALbasic

Ten en cuenta también que para cada comparación de dos elementos del arreglo que estamos ordenando, el nuevo algoritmo ha de ejecutar dos comparaciones más de lo que requiere el primer algoritmo. De modo que encontrar la posición en la que debe insertarse el nuevo elemento se hace más lento en comparación con el primer algoritmo. También ha de mantener el seguimiento de dos variables adicionales a medida que busca en la lista.

Ejercicios adicionales

Sería interesante implementar algunas de las otras ideas sobre cómo acelerar la ordenación por inserción. Crea cada una de ellas como una clase independiente, y conéctalas con el framework de pruebas de modo que puedas comprobar cuál es la más rápida con listados grandes. Sería sorprendente si alguna de las otras ideas sobre el modo de mejoar el algoritmo no fuesen realmente más rápida.

Dichos experimentos sólo constituyen un ejercicio académico (si bien un buen ejercicio). En la próxima lección probaremos un algoritmo distinto denominado Ordenación Rápida y que es mucho más rápido para una lista aleatoria...

Page 96: Curso REALbasic

Lección 16 Ordenación Rápida

Objetivo

En esta lección veremos la ordenación rápida (Quicksort) uno de los algoritmos más rápidos para la ordenación de propósito general. Aprenderemos como funciona, lo codificaremos en REALbasic y lo compararemos con los otros algoritmos de ordenación que hemos desarrollado.

Cómo funciona la ordenación rápida

Comenzaremos moviendo el primer elemento de la lista a la posición final, en el mismo proceso haciendo que todos los elementos en cualquiera de los lado sean todos los bajos (a la izquierda) y los altos (en la derecha).

Comenzaremos comparando el primer elemento con el elemento situado junto a él, y el elemento que está al final de la lista:

Figura 48. La situación inical.

(Captura Figura 48)

Moveremos x a la derecha e y a la izquierda, hasta que x apunte a un elmeneto mayor que 5 e y apunte a un elemento inferior a 5. A continuación intercambiaremos los elementos a los que estén apuntando x e y:

Figura 49. El primer intercambio.

(Captura Figura 49)

Repite este proceso, moviendo x e y hacia otros a medida que avanzamos:

Figura 50. Otro intercambio posterior.

(Captura Figura 50)

Este proceso continúa hasta que xy. Entonces intercambiamos el primer elemento (5) con el elemento situado en la posición anterior a x:

Figura 51. Completando la primera pasada.

(Captura Figura 51)

¿Hemos terminado?

El 5 está ahora en la posición final, y las sublistas a ambos lados del 5 son inferiores en todos los casos (a su izquierda) o bien superiores a 5 (en su derecha):

Figura 52. Hemos reducido el problema a dos problemas más pequeños.

(Captura Figura 52)

Page 97: Curso REALbasic

Deberías de ver que ahora simplemente podemos emplear el mismo proceso de forma recurrente para ordenar ambas listas más pequeñas.

El Proyecto

Abre el proyecto 16_QuickSort. Abre SortWindow en su Editor de Ventana, haz doble clic sobre el botón Quicksort, define un punto de parada al final del manejador de evento Action, y traza el algoritmo durante algún tiempo.

1 Ejecuta el antiguo botón de ordenación sobre un rango interesante: quizá de 500 a 1.000 en pasos de 100.

En el ordenador del autor, el algoritmo Quicksort fue en torno a 40 veces más rápido en comparación con la ordenación por inserción...

2 Ahora pruébalo de nuevo con números más pequeños. Es posible que Quicksort sea incluso más lento para algunas listas pequeñas.

Ejercicios Adicionales

Puede hacerse que el algoritmo sea más rápido. El algoritmo Qucksort es bastante ineficiente con listas pequeñas. Lo que funcionaría mejor sería cambiar a otro algoritmo como a la ordenación por inserción para las listas pequeñas. Ten en cuenta que una lista de mayor tamaño quedará reducida a varias listas más pequeñas hacia el final del proceso, de modo que ordenar estas listas más pequeñas de una forma más rápida acelerará considerablemente el proceso.

Un gran ejercicio sería el de producir un programa que pudiese averiguar a qué tamaño de listado debería cambiarse a un algoritmo de ordenación diferente. Este podría ser un programa que midiese repetidamente la ordenación de un gran arreglo, aumentando el tamaño del listado ordenado mediante inserción, hasta que el tiempo comenzase a ser inferior.

Page 98: Curso REALbasic

Lección 17 Excepciones, Constructores, Sobrecarga

Objetivo

Tomaremos un respiro de los algoritmos complejos. Echaremos un vistazo a un asunto de envergadura (Excepciones) y, de paso, también veremos algunas cuestiones adicionales (Constructores y Sobrecarga).

Excepciones

La gestión de errores y otras situaciones inusuales con los mecanismos de control que hemos visto hasta ahora (bucles, condicionales if-then, y otros similares) pueden hacer que el código más simple termine siendo más complejo, dado que tienes que plagar el código con comprobaciones para cada uno de los posibles errores que puedan tener lugar o bien condiciones atípicas. Esto puede hacer que el código resulte más difícil de desarrollar y también de mantener.

Las Excepciones suponen una respuesta elegante a este problema, proporcionando un modo controlado de saltar fuera del actual flujo de control.

He aquí un ejemplo. Pongamos por caso que este sea el flujo de control normal en alguna parte de un programa (El Método A llama al Método B que llama... al Método D y vuelve al Método C...):

Figura 53. El flujo de control normal.

(Captura Figura 53)

Ahora, ¿que hacemos cuando...?

Figura 54. Tiene lugar un problema...

(Captura Figura 54)

¿Ocurre un problema aquí?

Este problema puede ser un error en tu código, o quizá pueda tratarse de algo fuera de tu control (quizá estuviésemos a punto de abrir un archivo, pero ha sido borrado desde la última vez que lo comprobamos). Continuemos con el ejemplo. En este caso, el error tiene lugar cuando intentamos accede al elemento de un índice que no existe. En REALbasic dicha acción lanza una excepción:

Dim A(4) As integer

A(5) = 7

...y el código salta desde donde se estuviese ejecutando al manejador más cercano.

Cuando se lanza una excepción, REALbasic sigue un flujo de control muy distinto en comparación con la ejecución línea a línea. Busca el Manejador de Excepción coincidente más próximo. Este puede encontrarse al final del método en curso...

Page 99: Curso REALbasic

ReadElizaLine InputFrom, ElDelimiter, RespDelimiterWendInputFrom.Close

Exception E As OutOfBoundsException MsgBox “Ese valor está fuera de rango.” Quit

...o en el primer método que contenga dicho manejador, comenzando desde la parte inferior de la actual cadena de llamadas, y continuando hacia arriba por dicha cadena:

• El Método A llama a...• El Método B llama a...• El Método C llama a...• El Método D

Las excepciones se lanzan automáticamente cuando va algo mal:

Dim A As MyObjectA.MyMethod

Oops, A aun es Nil en este punto.Esto supone una execpión del tipo NilObjectException.

...o bien puedes lanzar una excepción tú mismo:

Raise New NilObjectException

Aunque probablemente no quieras lanzar una NilObjectException de forma deliberada.

Incluso puedes inventarte tus propias excepciones, y que son simplemente subclases de la clase RuntimeException. Puedes añadir tus propios métodos y propiedades en ellas, tal y como harías con cualquier clase, y lanzarla cuando quieras tratar con alguna circunstancia inusual:

Raise New MyExcepcion (“Estás de broma.”)

(El argumento del Nuevo comando en este caso forma parte de un constructor, algo que aprenderemos en un momento).

Es importante darse cuenta de que las Excepciones son objetos, teniendo una clase, con todos sus detalles:

• Pueden formar parte de una jerarquía de clase.• Los manejadores pueden atrapar las excepciones de superclases o subclases;• Puedes añadir métodos y propiedades a ellos;• Pueden incorporar mensajes de error y otra información; y• Pueden realizar una acción

Un manejador de execpción puede atrapar todas las excepciones:

Exception

Page 100: Curso REALbasic

...

O puede recibir el objeto de excepción como argumento:

Execption E...

Y también puede atrapar la excepción sólo si es de un tipo concreto:

Exception E As NilObjectException...

Puedes tener múltiples manejadores de excepción al final de un método. Este ejemplo proporciona manejadores específicos para dos excepciones en particular, y a continuación un manejador general que atrapará cualquier otro tipo de excepción:

Exception E As NilObjectException...

Exception E As OutOfBoundsException...

Exception...

Cuando existen múltiples manejadores como este, se ejecutará la excepción coincidente que esté más próxima a la parte superior del código.

Los anteriores manejadores de excepción ocurren al final de los métodos en los que tienen lugar. También puedes crear un bloque manejador de excepción en cualquier parte de un método, utilizando para ello la construcción Try-Catch-Finally-End Try, y que tiene el siguiente aspecto:

...Try ...Catch ...End Try...

Esto sería equivalente al primero de los anteriores ejemplos, dado que atrapa todas las excepciones que ocurren entre el Try y el Catch. También puedes recoger la excepción propiamente dicha en una variable:

...Try ...Catch E ...End Try...

Page 101: Curso REALbasic

Y puedes atrapar sólo las excepciones de un tipo concreto:

...Try ...Catch E As NilObjectException ...End Try...

Otra opción que tienes con este tipo de bloques consiste en proporcionar el código que debe ejecutarse después del bloque Try, tanto si se produce una excepción como si no. Este utiliza la sección Finally:

...Try ...Catch E As NilObjectException ...Finally ...End Try...

Siempre deberías de pensar sobre cualquier cosa de la que el bloque Try-Catch debiera de asegurarse después de que haya finalizado (por ejemplo, debe cerrar cualquier archivo que hubiese abierto), y situarlo en el bloque Finally.

Ten en cuenta que a diferencia del bloque Exception, puedes tener bloques Try-Catch anidados (lo que significa tener bloques Try-Catch dentro de otros bloques Try-Catch).

Manejador de Excepción por omisión

Si la excepción producida no encuentra ningún manejador coincidente, entonces REALbasic proporciona un manejador por omisión:

• En el IDE, REALbasic marcará la línea en la que se ha producido la excepción, y mostrará un mensaje sobre el tipo de excepción que se ha producido (probablemente ya lo hayas visto en varias ocasiones):

Figura 55. Manejador de Excepción por omisión en el IDE.

(Captura Figura 55)

• Si compilas tu programa y lo ejecutas fuera del entorno de desarrollo (que por lo general será tu objetivo), entonces cuando se produzca una excepción REALbasic mostrará un mensaje de error y saldrá:

Figura 56. Manejador de Excepción por omisión en una aplicación compilada.

(Captura Figura 56)

Page 102: Curso REALbasic

Seguramente quieras evitar que REALbasic muestre un mensaje como este, de modo que es muy importante aprender a manejar las Excepciones.

REALbasic proporciona un modo adicional de manejar las excepciones: una excepción que no se gestiona en ninguna otra parte producirá un evento UnhandledException en App. Siempre deberías proporcionar un manejador de este tipo en una aplicación que proporciones a otro, de modo que utilice una forma más elegante de tratar las excepciones no gestionadas frente al diálogo mostrado por REALbasic.

Lo visto hasta ahora ha supuesto un primer vistazo sobre las Excepciones; ahora las examinaremos en acción.

La Excepcional Eliza

1 Abre el proyecto incluyido, Exceptional Eliza.

Vamos a hacer que la rutina de parseado8 de Eliza pueda gestionar varios errores en el archivo. Mediante el uso de las Excepciones, podremos mantener prácticamente intactas las rutinas “normales” de parseado, al tiempo que incorporaremos un comportamiento muy distinto en las circunstancias “excepcionales”.

2 Examina la clase File Parse ErrorAdvierte que está pensada como superclase abstracta (no hace nada por sí misma; cuando llamas a su método Announce sólo estará realmente gestionado en la subclase como evento).

3 Examina la clase NaughtyWordErrorFíjate en concreto en el método NaughtyWordError. Se trata de un método especial denominado constructor.

Constructores y DestructoresUn constructor es simplemente un método con el nombre Constructor o, com en este caso, con el mismo nombre de la clase. Cualquiera de estos métodos se ejecutará automáticamente durante la creación de un objeto de ese tipo.

Ten en cuenta que un constructor puede tener argumentos, y que puedes proporcionar como argumentos mediante el comando New (tal y como hicimos anteriormente).

También puedes tener un destructor, y que se ejecuta cuando un objeto deje de estar referenciado por cualquier variable (y que no seguirá existiendo por ninguna otra razón; una Window (ventana) es un ejemplo de algo que continuará existiendo incluso en en el caso de que nada más haga referencia a ella), de modo que REALbasic pueda reclamar el espacio que ocupe en memoria. Antes de que lo haga, llamará al destructor del objeto en el caso de que exista uno, de modo que puedas hacer cualquier tarea de “limpieza” que desees en tu objeto, en el caso de que sea neceario.

Un destructor es un método que no tiene parámetros y tampoco tipo devuelto, y que tienen el nombre Destructor.

8 El parseado consiste en tomar una cadena para extraer de ella la información estructurada. Los humanos lo hacemos cuando comprendemos una frase en español, y los ordenadores lo hace con los archivos.

Page 103: Curso REALbasic

Ten en cuenta que desde el punto de vista técnico, los constructores y los destructores no son necesarios. Puedes escribir cualquier tipo de programa sin tener que utilizarlos. Pero resulta una buena idea utilizar un constructor en cualquier clase donde resulte apropiado, dado que contribuye a evitar que se cometan muchos errores.

• Examina la clase InvalidLineError

Observa en concreto los dos constructores. El uso de dos o más métodos o funciones con el mismo nombre se denomina sobrecarga (overloading) del método.

Sobrecarga (Overloading)Podemos tener más de un método o función con el mismo nombre, siempre y cuando como mínimo uno de sus argumentos sea de un tipo distinto, o bien no tengan la misma cantidad de argumentos. Ten en cuenta que REALbasic sólo podrá elegir una versión del método de cualquier llamada en particular, dado que los argumentos sólo se corresponderán con una verión.

La sobrecarga es particularmente común en los constructores, pero deberías sentirte libre de utilizar la sobrecarga siempre que sea razonable (en las siguientes lecciones veremos una amplia variedad de ejemplos correspondientes a la sobrecarga). Ten en cuenta que en el caso de los constructores, la sobrecarga es simplemente una cuestión de conveniencia. No representaría una gran diferencia el hecho de tener métodos con diferentes nombres. Pero deberías de utilizar la sobrecarga cuando tengas diferentes formas de solicitar una misma operación sobre diferentes tipos o cantidad de argumentos.

Límites de la SobrecargaEl compilador actual de REALbasic tiene dos limitaciones relacionados con la sobrecarga y que deberías de tener presentes:

• Una clase y su subclase no se consideran de diferente tipo cuando se evalúa entre versiones de un método sobrecargado. De modo que si Thing es una superclase de SubThing, entonces no habrá diferencia alguna entre las siguientes definiciones de método:

Sub MyMethod(t As Thing) … End Sub

Sub MyMethod(t As SubThing) … End Sub

• REALbasic busca una correspondencia para la llamada de un méotod a través de la jerarquía de la clase; y si un método está definido en una clase, no verá una versión diferente de dicho método definido en su superclase. La solución consiste en sobreescribir el método de la superclase en la subclase y realizar desde este de nuevo una llamada explícita al método de la superclase. En una lección posterior veremos cómo realizar una llamada al método de una superclase.

Echa un vistazo a StringList. Se trata de cosas que ya hemos visto anteriormente.

Page 104: Curso REALbasic

Examina el módulo BadThings. Una característica de los módulos que no hemos visto anteriormente es que pueden incluir constantes, y que son como variables cuyos valores no se pueden modificar.

Es buena idea utilizar constantes asignadas a nombres como en este caso para cualquier tipo de valor fijo que vaya a utilizar tu programa, dado que de este modo sólo tendrás que cambiar el valor en un único sitio. En este caso, se encarga de contener una lista de las palabras indebidas que no puedes tener en el archivo de respuestas.

Examinemos ahora los cambios en la clase StringResponder.

Advierte que el código encargado de parsear una línea se ha movido a un método distinto, ReadElizaLine. Haz doble clic sobre el nombre del método en el panel de la izquierda del editor de código, y advierte que el método está declarado como Protected (protegido).

Métodos PrivadosAlgunos métodos formarán parte de la interfaz pública de una clase. Estas son las operaciones que define el Tipo de Dato Abstracto que implementa la clase (recuerda: un tipo está definido por lo que hace), y pueden ser llamados desde otras clases.

Otros métodos de una clase serán parte de las operaciones internas de dicha clase, y por tanto no están destinados a que puedan ser llamados desde fuera de la clase. ReadElizaLine es un buen ejemplo de este tipo de método.

Haciendo que el método sea Privado indica que el método no forma parte de la interfaz pública de la clase. Cuando un método es Privado, el compilador hace que sea posible llamar a dicho método desde fuera de la clase. De igual modo, las subclases de la clase no heredan el método.

Otra alternativa es el método Protegido (Protected). Al igual que los métodos Privados, los métodos Protegidos no pueden ser accedidos desde fuera de la clase, pero las subclases de la clase sí pueden heredar los métodos Protegidos.

De vuelta a las ExcepcionesTen en cuenta que los comandos Raise generan el objeto justo en el comando, y cómo se pasan los argumentos a los constructores de la clase. Advierte también que New se está comportando ahora obviamente como una función.

Observa las ubicaciones de los comandos Raise y los manejadores de método (utiliza el área Buscar en la barra de herramientas principal para encontrar estas llamadas). Observa que tenemos un manejador tanto para la clase base FileParserError (en LoadFromFile) como en la subclase NaughtyWordError en ReadElizaLine.

Abre el archivo Responses.txt y observa si puedes encontrar dos posiciones en las que tendría lugar las excepciones.

1 Ejecuta el proyecto, activa puntos de parada en todos los lugares en los que puede producirse una excepción, y avanza paso a paso para ver qué ocurre en cada caso.

Page 105: Curso REALbasic

Observa que la excepción correspondiente al manejador NaughtyWordError (en el método ReadElizaLine) provoca que el proyecto salte la línea, para continuar parseando el archivo. Por otra parte, el punto de parada correspondiente a FileParseError (en LoadFromFile) aborta el proceso de parseado. Activa el comentario para el comando Quit en el manejador FileParseError para hacer que esto sea aún más claro.

2 Activa el comentario para el manejador NaughtyWordError, y observa el comportamiento cuando el proyecto esté en funcionamiento.

3 Después de haber comentado el manejador NaughtyWordError, mueve el manejador FileParseError al método ReadElizaLine, pero eliminando el comando Quit.

Comprueba qué ocurre ahora cuando tiene lugar el error.

Ejercicios AdicionalesSi comentas todos los comandos Quit en todos los manejadores de excepciones y ejecutas el programa, mostrará un par de alertas funcionando a continuación. Pero prueba a introducir una frase que no se corresponda con ninguna de las respuestas enlatadas (de modo que deba obtener una respuesta aleatoria; un buen ejemplo podría ser “Hola”). El programa saldrá con una excepción OutOfBoundsException. Puedes solucionar este problema arreglando el archivo Responses.txt, pero también deberías de lograr que el programa gestionase mejor este problema. Añade el manejador correspondiente a OutofBoundsException.

Otro ejercicio interesante sería dejar que las Excepciones se registrasen a sí mismas (digamos en una clase ExceptionLog), y que se añadiesen al final del proceso de parseado, antes de que el proyecto avanzase. En el caso de que no resulte obvio, el mejor sitio para registrar la generación de un objeto sería en su constructor...

Page 106: Curso REALbasic

Lección 18 Enumerador de Archivos

Objetivo

Tomaremos un respiro del aprendizaje de nuevos conceptos y cogeremos algunos de ellos para ponerlos en práctica en un proyecto útil.

Una clase útil: un Enumerador de archivos

Hemos absorbido una buena cantidad de conceptos durante el curso. Pasemos a utilizar dichas características para diseñar una clase muy útil. Esta será un enumerador de archivos flexible.

Enumerar es un término informático que se refiere a pasar por un conjunto de cosas una a una (un término similar con el que también puedes encontrarte es atravesar). Crearemos una clase con la que puedas referirte a una carpeta o archivo, y nuestra clase generará un evento para cada elemento de dicha carpeta.

Aspectos del diseño

Existen unas cuantas formas de enfocar lo que queremos llevar a cabo en este proyecto:

• Podemos crear una clase que atraviese parte del árbol de archivos, proporcionando una a su subclase la oportunidad de procesar cada archivo a medida que lo vaya haciendo; o

• Podemos crear una clase que llame de forma repetida a una clase receptor de archivo a medida que atraviesa el árbol de archivos; o

• Podemos hacer que la clase se pasiva, dirigida por llamadas repetidas desde el exterior, en las que se solicite el siguiente archivo.

El enfoque que utilizaremos será el primero; si quisiéramos, podemos ampliarlo para crear la segunda. El tercer enfoque tiende a ser más problemático dado que el llamante externo necesitaría comprobar el estado del recorrido de los archivos, comprobando por ejemplo si se ha finalizado dicho recorrido.

Antes de que nos pongamos a ello, deberías leer acerca de la clase FolderItem.

Profundidad frente a Anchura

Los archivos están dispuestos en una estructura de árbol compuesta por carpetas, subcarpetas, sub-subcarpetas, etc. Las Clases también son una forma parecida de árbol compuesto por clases, subclases, sub-subclases, etc. Las estructuras de árbol para almacenar la información son algo muy común en la programación de ordenadores, y su acceso requiere con frecuencia la enumeración de sus contenidos tal y como haremos en este proyecto.

Existen principalmente dos formas de recorrer una estructura de árbol:

• En un recorrido de profundidad en primer lugar, donde cada vez que encontremos una carpeta dentro de la carpeta actual, comenzaremos a enumerar de inmediato sus contenidos, tal y como haremos también cuando encontremos la primera carpeta dentro de dicha carpeta, etc. Regresaremos al resto de los elementos de una carpeta después de que hayamos enumerado los contenidos de su subcarpeta; o

Page 107: Curso REALbasic

• En un recorrido de anchura en primer lugar, enumeramos todos los archivos de la carpeta actual, enumerando a continuación los contenidos de cualquier carpeta que encontremos dentro de la actual.

Veremos ambas técnicas en accesión durante el proyecto de esta lección.

El Proyecto1 Abre el proyecto 18_FileEnumerator. Abre la clase FileEnumerator.Observa que la clase tiene un constructor, un método público, varios métodos privados y tres eventos nuevos.

Dos de estos eventos permiten que una subclase pueda determinar cómo utilizar la enumeración (DepthFirst) y permite que una sublcase pueda interrumpir la iteración (Stop). Ambos funcionan correctamente con los valores devueltos por omisión. De modo que en realidad una subclase sólo necesita implementar un manejador de evento (EnumerateItem).

2 Abre la clase FileCounter y observa lo sencillo que resulta crear una clase hija encargada de contar los archivos de esa ubicación.

3 Regresa a la clase FileEnumerator y examina la estructura recursiva del proceso correspondiente a la enumeración de archivos.

Observa cómo se realiza la enumeración de tipo “primero en profundidad” enumerando recursivamente de forma inmediata cualquier carpeta que se encuentra, mientras que una enumeración del tipo “primero a lo ancho” se realiza enumerando en primer lugar todos los elementos de una carpeta y posteriormente enumerando de forma recursiva los contenidos de cada una de las subcarpetas.

4 Añade un punto de parada en el método Enumerate del FileEnumerator. Ejecuta el programa y elige una carpeta que contenga una pequeña cantidad de archivos y una o dos subcarpetas. Traza la ejecución. A continuación comenta el comando:

Return True

En el manejador de evento DepthFirst en la clase FileCounter, ejecuta el programa y elige la misma carpeta.

TrueItemObserva que cuando seleccionamos un elemento en concreto en una carpeta, utilizamos la función TrueItem en vez de la función Item correspondiente al FolderItem. La diferencia reside en el tratamiento realizado con los atajos (los atajos se denominan alias en los Macintosh): cuando Item se corresponde con un atajo este se tratará como el archivo al que apunta, mientras que TrueItem se tratará como un archivo de atajo propiamente dicho.

Es importante tener en cuenta esto, dado que si seguimos los atajos hasta su fuente, sería posible quedarse enredado en una estructura de directorio en bucle, donde un atajo apuntase a una de las carpetas en las que estuviese contenido, causando por tanto que el proceso de enumeración continuase funcionando de forma indefinida.

Page 108: Curso REALbasic

Claramente sería mejor si siguiésemos los atajos hasta sus archivos originales y de hecho es lo que haremos en un proyecto posterior. De hecho, notificaremos a la subclase mediante un evento que hemos encontrado un atajo y dejaremos que la subclase decida si lo seguimos o no.

Es más, crearemos una clase que no se quede detenida en un bucle, independientemente de lo que haga la subclase. Sin embargo, hacer esto supone una sorprendente cantidad de trabajo adicional, y tendremos que crear en las siguientes lecciones un poco de infraestructura adicional para conseguir que funcione correctamente.

Conceptos utilizados en este proyectoObserva que este proyecto utiliza recursión. Esta es una forma natural de afrontar el problema, dado que los datos involucrados tienen una estructura recursiva (una carpeta es el mismo tipo de “cosa” que la carpeta en la que está contenida).

Observa que hemos creado una clase de la que se debe crear una subclase para que pueda realizar algo de utilidad. Ten en cuenta también que hemos creado una clase flexible que puede utilizarse de una variedad de formas. Armados con esta clase, puedes crear con rapidez una amplia variedad de utildiades de archivo. Por ejemplo, advierte la poca cantidad de código adicional necesario a la hora de utilizar esta clase para crear un programa que cuente archivos y carpetas.

Ejercicios adicionalesHay una amplia variedad de potenciales ejercicios que podrían realizarse a partir de estos inicios. Prueba a desarrollar una utilidad de archivos útil, Quizá algo que liste todos los archivos contenidos en una carpeta.

Una mejora interesante podría ser la de proporcionar a la subclase la capacidad de decidir si debería de enumerarse un atajo o bien el archivo original al que apunta. Probablemente la mejor forma de hacerlo sea utilizando un evento especial para un alias: EnumerateAlias(f as FolderItem, g As FolderItem) As Boolean. El método debería devolver True para indicar que el destino debería enumerarse en el caso de que fuese una carpeta, y False si no lo es. Deberías pensar sobre el problema de la estructura de directorio en bucle. No es muy difícil crear una solución simple, pero sí llevará un poco más de trabajo conseguir algo que no se ralentice en exceso a medida que crezca la estructura del directorio observado.

Page 109: Curso REALbasic

Lección 19 Estructuras de Datos

ObjetivoQueremos conseguir una versión del Enumerador de archivos que pueda gestionar los atajos de carpetas sin que se vea atrapado en los bucles que pueda resultar de dicha acción.

El único modo práctico de evitar este problema de bucle consiste en mantener un listado con todas las carpetas que ya se han enumerado. Antes de enumerar una carpeta, comprobaremos si ya figura en el listado.

Actualmente, un ordenador moderno puede tener miles de carpetas y crear y buscar dicho listado cada vez que encontrásemos una carpeta podría llevarnos al terreno en el que nos preocupemos de la eficiencia9.

Si nos limitamos a mantener este listado como cualquiera de las estructuras de datos simples que hemos visto hasta ahora (pongamos por caso un arreglo de cadenas), y buscamos dicho listado de principio a fin (denominado búsqueda lineal), entonces este proceso de búsqueda ralentizaría todo el proceso de enumeración de forma significativa a medida que aumentase el propio listado.

Con todo esto en mente, esta lección examinará otras estructuras de datos diferentes para almacenar y buscar un listado de cadenas. Observa que REALbasic incluye de serie una estructura de datos que puede hacer lo que necesitamos: el Diccionario (Dictionary). Pero aprenderemos mucho más implementando nuestra propia solución.

El ProyectoSiguiendo con las ideas de las que hemos estado hablando, desarrollaremos una clase que llamaremos String Accumulator, y que se encarga de mantener un listado de cadenas. Dicha clase tienen un método principal, sobre el que enviarás una cadena. Si la cadena ya está en el listado, devuelve True; de lo contrario la cadena se añade al listado y devuelve False. Por tanto, podemos suministrar a dicho método la ruta del archivo que estamos a punto de seguir, y éste nos indicará si ya lo hemos buscado anteriormente.

En esta lección examinaremos tres estructuras de datos diferentes para almacenar el listado y dos algoritmos diferentes para buscar en cada una de estas estructuras de datos. Utilizaremos un framework que nos permitirá comprobar la velocidad de las estructuras de datos y de los algoritmos.

Asegúrate de que pasas algo de tiempo pensando sobre el modo en el que implementarías el acumulador de cadenas, utilizando para ello lo que ya hemos aprendido hasta ahora.

Como hemos visto en las anteriores lecciones, no tenemos un modo práctico para comprender exactamente lo que REALbasic está haciendo de forma interna con nuestro

9 En realidad esto aun depende de las consideraciones relacionadas con tu situación. Si creasemos una utilidad para hacer algo de forma muy ocasional, o en el caso de que procesar cada uno de los archivos fuese a llevar una cantidad de tiempo considerable, en cualquier caso, entonces no valdría mucho el esfuerzo optimizar este tipo de cosas. Sin embargo, en el caso de que estuviésemos creando algo de propósito general y que fuese reutilizable, entonces podríamos optimizarlo una vez y utilizarlo una y otra vez, de modo que mereciese la pena el esfuerzo realizado para acelerarlo el máximo posible.

Page 110: Curso REALbasic

código. Cuando se trata de cuestiones relacionadas con la eficiencia, el único modo fiable para determinar cuál es el código más eficiente para una tarea dada consiste en configurar una prueba.

1 Abre el proyecto 19_StringAccumulatorLa ventana principal del proyecto proporciona tres botones. El superior te permite una prueba de tiempo en la que se comparan distintas implementaciones del acumulador de cadenas. El botón inferior te permite ejecutar una prueba simple sobre un acumulador. De forma deliberada, ninguno de los botones proporciona una salida: este proyecto es simplemente un framework mínimo pero funcional que un desarrollador podría llevar a la práctica para realizar comparaciones de tiempo. Puedes limitarte a añadir puntos de parada en un método para determinar su comportamiento. Por ejemplo, en el caso de que quisieras realizar pruebas de rendimiento podrías incluir un punto de parada en la última línea del botón superior y examinar a continuación los contenidos del array Timings.

2 Comienza examinando la clase realmente simple StringAccumulator.

3 Examina ahora la clase ArrayStringAccumulator.Quizá esto sea lo que habías previsto cuando pensaste sobre el modo de crear la clase StringAccumulator.

Búsqueda binariaUna forma de acelarar la búsqueda sobre un listado ordenador es mediante una búsqueda binaria, donde divides la lista por la mitad repetidamente. Comienzas buscando por el elemento de la mitad en el listado y restringes la búsqueda sobre la primera mita del listado, en función de cuál sea el elemento que estés buscando. A continuación buscas en la mitad de dicho sublistado y vuelves a dividirlo por la mitad, repitiendo este proceso una y otra vez.

1 Exmina el código en el BinaryArrayStringAccumulator.

2 Examina el código correspondiente al RunButton de la ventana. Comenta el código encargado de añadir todo excepto el BinaryArrayStringAccumulator y el ArrayStringAccumulator.

3 Asegúrate de tener un punto de parada al final del método.

4 Ejecuta el proyecto y haz clic en el botón. Al finalizar examina los valores del array Timings.Te sorprenderás al comprobar la rapidez de la búsqueda binaria.

Observar que una búsqueda lineal a través de N elementos realizará de media una serie de 1/2N comparaciones, mientras que una búsqueda binaria realizará de media en torno a log2N comparaciones.

Antes de que continuemos, deberías revisar la presentación PopwerPoint: About Memory Management.

Teniendo en cuenta lo que has aprendido sobre la gestión de la memdoia, deberíamos tener en cuenta el uso de un Listado Enlazado creado a partir de una serie de objetos independientes. Sería imposible hacer una búsqueda binaria en este tipo de listado, pero insertar un elemento en este tipo de listado puede realizarse en una cantida de tiempo

Page 111: Curso REALbasic

constante, independientemente de cuá sea la longitud del listado. Lo que no está claro es si este tipo de listados serían más rápidos en comparación con los otros listado que hemos vistado hasta ahora.

5 Examina el código de la clase ListStringAccumulator2, y comenta de nuevo el código del botón y ejecuta el proyecto.

Ouch. La nueva clase aun tiene la mitad de la velocidad en comparación con ArrayStringAccumulator, deja sólo el array de la búsqueda binaria.

A continuación vamos a examinar algunas estructuras de datos en árbol. Estas se corresponden con tipos de estructuras de datos muy comunes y veremos la más simple de ellas: un Árbol Binario.

Sobre los Árboles BinariosLa información en un árbol binario está contenida en Nodos que están unidos por las ramas (estos serán objetos para los nodos y referencias a otros objetos como ramas):

Figura 57. Ejemplo de árbol binario

(Captura Figura 57)

En la Figura 57, los numeros están contenidos en los nodos y las flechas representan las ramas. Cada nodo tienen una rama menor que y otra mayor que (cualquiera de ellas puede ser Nil).

Para encontrar algo en un árbol binario, sólo has de seguir sus ramas:

Figura 58. Ubicar un valor siguiendo las ramas.

(Captura Figura 58)

Para insertar algo, encuentra dónde debería de estar e insértalo en dicha posición:

Figura 59. Inserta algo en la posición correspondiente.

(Captura Figura 59)

Un problema potencial es que la ʻformaʼ del árbol depender del orden en el que se vayan añadiendo los nodos:

Figura 60. Un árbol binario no balanceado.

(Captura Figura 60)

Esto quizá resulte peor que una lista enlazada...

El árbol en un árbol binario no balanceado. El 4 no está balanceado y funcionará tan mal o peor de lo que lo haría una lista enlazada.

6 Exmina el código encargado de hacer funcionar la clase BinaryTreeAccumulator. Ejecútalo mediante el botón TestOneButton y trázalo a medida que lo haces.

Page 112: Curso REALbasic

7 Examina la clase no recursiva BinaryTreeAccumulator2.Si no estás seguro sobre cómo funciona, trázalo desde el botón TestOneButton.

8 Reordena el método RunButton de modo que podamos comparar los tiempos correspondientes a las dos clases BinaryTreeAccumulator y el BinaryArrayStringAccumulator. Comenta el manejador StackOverflowException. Ejecuta la aplicación.

Wow. BinaryArrayStringAccumulator aun es varias veces más rápido en comparación con cualquiera de las otras opciones probadas.

Otras opcionesEl BinaryArrayStringAccumulaotr es razonablemente rápido; en realidad varias veces más rápido en comparación con la lista de búsqueda lineal. Será suficiente para nuestro proyecto de enumeración de archivos.

En cualqueir caso, resulta interesante tener en cuenta cómo podríamos hacer que nuestro ArrayStringAccumulator fuese más rápido. Podríamos usar un tipo de árbol más inteligente que siempre estuviese balanceados: un árbol 2-3. También existe una fuente de ineficiencia en todas nuestras clases: REALbasic no sabe que nunca vamos a cambiar el tamaño de nuestras cadenas, o bien que vamos a necesitar almacenar una gran cantidad de cadenas. Tomando el control sobre nuestra propia gestión de la memori a un nivel más bajo (mediante el uso de la clase MemoryBlock) podríamos lograr que todas estas clases fuesen más rápidas. Utilizando un esquema como este, podríamos llegar a la conclusión de que la versión BinaryArrayStringAccumulator deja de ser la más rápida.

Cualquier libro de texto sobre estructuras de datos debería describir el árbol 2-3; dado que constituye una de las estructuras de datos más básicas en ciencia de la computación. Por la misma razón, tampoco debería de resultar difícil encontrar una descripción del árbol 2-3 en línea. Sin embargo, existe una razonable cantidad de código involucrada en la implementación de un árbol 2-3, de modo que se sale un poco del objetivo de este curso. Del mismo modo, las características de almacenamiento de datos incluidas en REALbasic (particularmente la clase Dictionary) hacen que sea poco probable el hecho de que vayas a necesitar implementar una estructura de datos como esa para programa en REALbasic.

Otras mejorasDeberías leer la documentación de REALbasic sobre el MemoryBlock. Un MemoryBlock es una porción de memoria muy simple, básicamente como si fuese un arreglo de butes. Dado que es tan simple, también es muy rápida. REALbasic no precisa entrar en los problemas adicionales requeridos para hacer que las cadenas resulten más flexibles. Eto significa que puedes obtener provecho de tu conocimiento sobre el hecho de que las cadenas que estás almacenando no precisan cambiar su tamaño, de modo que sea más rápido trabajar con ellas. De igual modo puedes mantener incluso un árbol binario 2-3, o para el caso nuestra lista más simple, en un bloque de memoria de gran tamaño que haría todo el proceso sustancialmente más rápido.

Existe un modo razonablemente más simple, muy flexible y eficiente para acelerar prácticamente cualquier estructura de datos, denominado hashing. Emplearemos el hashing en una lección dentro de muy poco, y veremos una mejora sustancial sobre nuestro listado con búsqueda binaria. Mientras tanto, tenemos una clase que

Page 113: Curso REALbasic

proporcionará un rendimiento razonable para nuestro enumerador de archivos, de modo que volvamos de nuevo a nuestro enumerador de archivos en la siguiente lección.

Ejercicios adicionalesExisten una buena cantidad de ejercicios potenciales que pueden realizarse a partir de este punto.

Uno interesante sería un modo bastante simple de acelerar cualquiera de las clases basadas en arrays (arreglos): aumentar su tamaño de forma muy ocasional y en grandes cantidades. Esto involucraría el hecho de mantener un registro independiente sobre el tamaño reservado que se esté utilizando, resevando por ejemplo 1.000 o 10.000 elementos en el constructor y, pongamos por caso, duplicando el tamaño del array cuando se esté quedando sin espacio. Ten en cuenta que un constructor como este (al final del método SubRangeSearch, en este ejemplo):

Exception E As OutOfBoundsException ReDim TheStrings(2*Ubound(TheStrings)) SubRangeSearch(S, x, y)

Es perfectamente aceptable y ciertamente el modo más sencillo de llevar a cabo el cambio de tamaño del array.

Apéndice: Sobre la Gestión de MemoriaLa memoria de tu programa es básicamente un enorme arreglo de bytes. Un bute es cualquier número entre 0 y 255.

REALbasic se encarga de gestionar la memoria por ti. Puedes crear, destruir y cambiar el tamaño de las variables y de los objetos sin preocuparte sobre los detalles. Internamente, REALbasic está registrando:

• Dónde se almacena cada uno de los objetos;• Si un objeto aun está siendo utilizado;• Cuando se puede reclamar el espacio empleado por un objeto que ya no está siendo

utilizado:• Dónde se encuentra el espacio utilizado y el espacio libre; y• Una buena cantidad de otras cuestiones...

De modo que si cambias el tamaño de un arreglo de cadenas, REALbasic decide si el espacio que la variable tiene reservado resulta suficiente. Si dicho espacio no resulta suficiente, entonces el contenido de la variable se mueve al nuevo espacio y se reclama el antiguo espacio.

Después de crear y destruir una buena cantidad de objetos...

Figura 61. Una ilustración de memoria fragmentada.

(Captura Figura 61)

...la memoria se fragmenta; o se convierte en una mezcla de porciones llenas y libres. A continuación si pruebas a crear un objeto grande, o bien conviertes un objeto existente en otro mucho mayor, el ordenador ha de mover una buena cantidad de cosas con el objeto de hacer espacio.

Page 114: Curso REALbasic

De modo que si estás preocupado por la eficiencia:

• Cambia el tamaño de las cosas con poca frecuencia, en grandes porciones, en vez de hacerlo un caracter o elemento cada vez;

• Considera reutilizar objetos en vez de destruir un objeto y crear un nuevo objeto del mismo tipo;

• Considera realizar tu propia gestión de memoria para los objetos importantes empleando para ello los MemoryBlock.

Resulta evidente que los desarrolladores de REALbasic procuran protegernos de la necesidad de que tengamos que llevar a cabo este tipo de tareas, y de hecho lo han conseguido al precisar que este tipo de consideraciones resulten sólo importantes en situaciones de rendimiento muy crítico. Por ejemplo, si pides a REALbasic que cambie el tamaño de un array un elemento cada vez, en realidad no hará eso; en secreto cambiará el tamaño del array utilizando tamaños mayores, manteniendo un registro sobre la cantidad de espacio reservada y la que realmente estás utilizando.

Page 115: Curso REALbasic

Lección 20 Enumerador de Archivos 2

ObjetivoVamos a introducir los resultados de nuestro último proyecto en el proyecto enumerador de archivos que hemos visto en las anteriores lecciones, además de refinar un poco más el enumerador de archivos propiamente dicho. En concreto se trata de algo más ligero en comparación con lo visto en nuestro último proyecto.

Analizar el problemaComenzaremos pensando en nuestro objetivo: queremos que la clase FileEnumerator sea capaz de manejar los atajos de una forma inteligente. Debe ser capaz de hacer tanto trabajo como sea razonable, al tiempo que deja las principales decisiones en mano de la subclase (ya lo hemos hecho, por ejemplo, con la posibilidad de decidir si la enumeración debe realizarse primero en profundidad o a lo ancho). De modo que detengámonos a pensar un poco sobre el modo en el que podemos tratar los atajos.

Lo primero de lo que nos damos cuenta es que la subclase debería decidir si se debería de seguir el atajo hasta su original o no. La solución consiste en proporcionar un Evento FollowAlias que permita tomar dicha decisión a la clase.

Pero nos encontramos con otro, sutil, problema de diseño: ¿debería el archivo de atajo enumerarse como si se tratase de un archivo normal? Dado que pasaremos el FolderItem correspondiente al alias al evento FollowAlias, decidimos no pasar ese mismo FolderItem al evento EnumerateItem.

Esto es asi porque, si la subclase quiere procesar el alias junto con el resto de los archivos, puede hacerlo facilmente en el evento FollowAlias. Sin embargo, si por el contrario la clase quiere ignorar los atajos, entonces no es necesario comprobar cada uno de los FolderItem enumerados para ver si se trata de un atajo. En cuanto a los archivos apuntados por los atajos, los enumeraremos utilizando para ello el evento, EnumerateItem normal en el caso de que el evento FollowAlias devuelva True. Este diseño será flexible, pero sencillo de usar, independientemente de cuál sea el comportamiento que se haya deseado para los alias.

El otro problema con el que nos encontramos es cómo evitar que los atajos nos atrapen en los bucles. Ahora esto resulta sencillo de tratar: antes de que enumeremos una carpeta, comprobaremos si su ruta10 se encuentra en el listado de rutas correspondiente a las carpetas ya enumeradas. En el caso de que se encuentre en el listado, lo pasaremos por alto; de lo contrario, lo añadiremos al listado y procederemos a enumerar sus contenidos.

El Proyecto1 Abre el proyecto de enumeración de archivos correspondiente a la Lección 18, y examina donde se podría utilizar el acumulador de cadena para evitar los bucles provocados por los atajos.

2 Ahora abre la versión actualizada, 20_FileEnumerator2.Se ha reconstruido el proyecto para que se acomode con mayor comodidad a los nuevos objetivos. El mejor modo de separar la parte correspondiente al hallazgo de un atajo en

10 La ruta de un archivo es una cadena que especifica con exactitud dónde reside. Todos los archivos y carpetas tienen una ruta única.

Page 116: Curso REALbasic

un evento independiente consiste en utilizar una subclase. Sin embargo, el código necesario para ello precisa de algunos cambios en la clase FileEnumerator.

Del mismo modo, FileEnumerator se ha modifucado de modo que en vez de proporcionar la ruta a enumerar en un constructor lo pasamos ahora mediante la llamada a Enumerate. Esto permite que la subclase pueda reiniciar de forma recursiva el proceso de enumeración a medida que sea preciso.

Mientras que estamos haciendo cambios sobre esta clase, también hemos llevado la acción de encontrar una carpeta a su propio evento, proporcionando así a la subclase la oportunidad de sobreescribir la enumeración de la carpeta. Ten en cuenta que el comportamiento por omisión consiste en no enumerar la carpeta; la subclase debe devolver True en el evento EnumerateFolder en el caso de que se quieran enumerar los contenidos de la carpeta.

¿Por qué lo hacemos así? Porque no había un nombre mejor para un evento con la interpretación opuesta. SIempre es bueno elegir nombres autoexplicativos...

3 Examina la clase en la que añadimos el comportamiento correspondiente al manejo del alias.Observa cómo los mecanismos de los eventos nos permiten “reinterpretar” un evento: FileEnumerator enumera todo lo que encuentra mediante los eventos EnumerateFolder y EnumerateItem, sin importar si alguno de estos elementos se corresponden con alias. FileAliasEnumerator intercepta el evento EnumerateItem. y divide la enumeración del alias en un evento diferente.

4 Ejecuta el código y pruébalo sobre algunas carpetas de tamaño medio.La velocidad no está mal, incluso desde el depurador.

5 Por último, prueba a enumerar una carpeta realmente grande (por ejemplo el raíz de tu disco duro).Si tienes una buena cantidad real de carpetas anidadas, entonces puede que llegues a ver incluso un desbordamiento de la pila (aunque es poco probable).

En una lección posterior consideraremos el modo de resolver el problema correspondiente al desbordamiento de la pila.

Ejercicios AdicionalesPiensa sobre los tipos de utilidades que pueden desarrollarse en función de la capacidad de enumeración de archivos.

Antes de que lo hagamos en una lección posterior, es probable que quieras probar a crear una versión no recurrente de este proyecto.

Resultaría más claro devolver la capacidad FIleAliasEnumerator de la carpeta de nuevo a la clase principal.

Page 117: Curso REALbasic

Lección 21 Hashing

Objetivo

Combinamos una estructura de datos simple denominada tabla hash con el listado lineal más simple de una lección anterior. El resultado es una estructura de datos que es mucho más rápida en comparación con el arreglo de búsqueda binaria.

Sobre las tablas HashEl Hashing es una técnica muy simple y utilizada ampliamente para acelerar virtualmente cualquier estructura de datos. Debe combinarse con otra estructura de datos, puesto que lo realizado es separar los elementos a almacenar y buscar en varios grupos más pequeños; si bien aun tenemos que almacenar y buscar dichos grupos de algún modo.

El motivo por el que esto resulta más rápido es porque podemos dividir una estructura de datos mayor en varias estructuras de datos más pequeñas. La división es rápida, y requiere la misma cantidad de tiempo, independientemente de la cantidad de grupos que tengamos.

Los grupos se generan mediante la creación de un modo que permita generar de forma constante una clave hash a partir de cada uno de los posibles valores a almacenar. Este número hash debería de tener las siguientes propiedades:

• Debería generarse de forma razonablemente rápida;• Debería encontrarse en un rango razonablemente corto (en el rango del número de

grupos que quieras);• Debería de minimizar las colisiones (valores que resulten en la misma clave). Esto

significa que cada conjunto de valores “aleatorios” deberíasn distribuirse de forma equitativa, pero también que un conjunto de, por ejemplo, valores muy similares deberían generar números hash muy diferentes.

El segundo criterio es lo suficientemente abierto a la interpretación como para haber producido una cantidad razonable de debate en los círculos relacionados con la programación de ordenadores. Encontrarás una gran variedad de funciones de hashing en los libros de texto correspondientes a ciencias de la computación y también en Internet. El algoritmo que estamos utilizando es uno encontrado en Internet razonablemente robusto. Desafortunadamente, si bien la función de hash propiamente dicha es razonablemente sencilla, la matemática que se halla detrás de una función de hash es por lo general razonablemente compleja. Si estás interesado en los aspectos relacionados con la obtención de una función hash, consulta cualquier libro de ciencias de la computación. Uno de los mejores (sobre este y otros asuntos) es The Art of Computer Programming11.

Una cosa que ayuda con prácticamente cualquier función de hashing es tener un módulo primo (el número de grupos se denomina el módulo en la función de hash).

Actividades y Procedimientos1 Abre project 21_Hashing.

11 En este caso, deberías de consultar el Volumen 3, si bien todas las series consituyen una verdadera obra de arte. Knuth, D E, The Art of Computer Programming, 2nd Ed, Addison Wesley, Reading Mass, 1997 ISBN 0-201-89685-0.

Page 118: Curso REALbasic

2 Examina el código en el módulo Hash, y a continuación HashStringAccumulator y ListHashStringAccumulator.

Observa lo simple que es todo ello. Asegúrate de comprobar también cómo funcionan juntas HashStringAccumulator y ListHashStringAccumulator: en vez de que un evento pase de forma explícita de uno a otro, la subclase sólo tiene que configurar el índice de la tabla de contenidos hash. HashStringAccumulator puede emplear cualquier StringAccumulator en la tabla hash de forma polimórfica; de modo que una vez que la subclase ha configurado algún tipo de StringAccumulator en cada uno de los elementos de la tabla hash, su trabajo ya está hecho. Todo esto significa que todo lo que necesita proporcionar la subclase es un constructor.

Advierte también que tanto el HashStringAccumulator como los contenidos de su tabla hash son StringAccumulators, dado que ambos hacen lo mismo12.

El valor de la constante HashMod es uno razonable para una cantidad de datos relativamente pequeña como la utilizada en este proyecto. Para facilitar el seguimiento de lo que tiene lugar, cámbialo a un valor más pequeño (por ejemplo, 5). Ejecuta el proyecto y prueba varios valores de HashMod.

Probablemente también quieras probar a crear otras subclases de HashStringAccumulator y medir sus tiempos.

Ejercicios adicionalesUna buena prueba de tu compresión sobre los aspectos relacionados con el hashing consiste en ver si puedes crear una estructura de hashing de dos niveles (tablas hash que contengan tablas hash, y que en último extremo contenga alguna otra estructura como una lista). Utiliza algo de tiempo en pensar cómo podrías hacerlo.

Puntos adicionales si has dejado de pensar en ello, y te das cuenta de que necesitas utilizar valores HashMod distintos para dos niveles diferentes13

12 No se trata de una estrategia atípica. Esto es algo que puedes hacer como solución a un problema: tienes una clase que llama a un método sobre un único objeto de un tipo determinado. Posteriormente decides que quieres que llame a dicho método en un grupo de esos objetos. En vez de modificar la definición de la clase, puedes escribir una clase intermedia del mismo tipo como la única clase a la que llames ahora, y dejar que sea tu nuevo ʻadaptadorʼ quien se encargue de pasar la llamada al resto del grupo de objetos.

13 En la práctica siempre será mejor contar con una tabla hash grande frente al uso de múltiples niveles de tablas hash. Una tabla es más sencilla y sólo tienes que ejecutar una vez el cálculo del número hash.

Page 119: Curso REALbasic

Lección 22 Comprobación de redundancia cíclica

Objetivo

Esta lección explora el extaño mundo de la aritmética binaria, mediante la implementación de un algoritmo muy importante: la comprobación de redunancia cíclica rápida (CRC).

La teoría relacionada no es muy compleja, pero precisa del aprendizaje de un nuevo tipo de matemática. Es considerablemente más sencillo que, pongamos por caso, la aritmética básica que aprendiste en primaria; pero aun así resulta un poco desconcertante, de modo que si no te sientes como en casa, no te preocupes... es normal. Digamos que se trata de una lección opcional.

Vamos a llevar a cabo esta lección porque las Comprobaciones de Redundancia Cíclica (CRC) son muy importantes, pero también porque pensamos que se trata de una buena lección sobre la aritmética binaria. Existen varios cientos de sitios web y libros en los que puedes aprender aritmética binaria básica, de modo que hemos pensado que no sería buena idea cubrir en este espacio el mismo tipo de contenido. En vez de ello, tomaremos una lección diseñada para que puedas comprender los fundamentos sobre el sentido de todos esos unos y ceros que gobiernan el funcionamiento de tu ordenador.

Adicionalmente, en el proyecto también se utiliza una técnica utilizada ampliamente para la aceleración de los cálculos CRC.

Aprender matemática binaria básicaLa matemática binaria es matemática en base 2. Funciona del mismo modo que la matemática en base 10 a la que estás acostumbrado, pero en la que sólo puedes utilizar 1 y 0; y en vez de los dígitos de los números que representan las unidades, decenas, centenas, millares, etc, estos representan unos, doses, cuatros, ochos, etc. Si a lo largo de la lección consideras que necesitas más información sobre cómo funciona la matemática binaria, prueba a realizar una búsqueda en google sobre “matemática binaria”.

¿Qué es la Comprobación de Redundancia Cíclica?Es bastante frecuente que quieras comparar dos fragmentos de información que se encuentran separados en el tiempo y en el espacio, en una situación donde sólo tienes una o incluso puede que ninguna de estas dos piezas. Es posible que hayas transmitido algo por la red, o que la hayas almacenado durante algún periodo de tiempo y de algún modo, y ahora quieras determinar si lo que tienes es lo mismo que el original.

Un CRC es un modo de hacerlo: es una ʻfirmaʼ resumida generada a partir de la información, de un modo que si la información ha sufrido algún tipo de modificación, entonces la firma será diferente.

Dado que el CRC es generalmente más corto que la información de la que está derivado, resulta inevitable que varios bloques de datos generen el mismo CRC. Lo que queremos, no obstante, es garantizar que el CRC no será el mismo teniendo en cuenta los patrones de errores esperados (como el cambio en el valor de terminados bits, cambio de valor en ráfagas de bits, etc.)

Page 120: Curso REALbasic

La matemática está un poco alejada del objetivo de esta lección, pero no resulta muy difícil probar que el CRC que vamos a usar aquí cumple con esos tipos de criterio. Veamos por ejemplo,

http://www.cs.williams.edu/~tom/courses/336/outlines/lect7_2.html

como una prueba de esto.

Matemática con Polinómios Módulo 2En esta lección vamoa combinar los unos y ceros nativos del ordenador en algo que son casi números. Técnicamente, son polinomios módulo 214. Son prácticamente números porque podemos realizar operaciones con ellos que son equivalentes a las operaciones a en las que acostumbras emplear números.

Un polinomio módulo 2 es una cadena de unos y ceros, justo como cualquier número binario normal. Para simplificar las cosas, nos referiremos a ellos a partir de ahora como números CRC.

Con los números CRC puedes realizar las habituales operaciones de suma, resta, multiplicación y división, del mismo modo que realizas dichas operaciones sobre los números binarios normales, con una excepción: puedes ignorar el acarreo15.

La única operación que necesitamos pra el CRC es la división. De hecho, el cálculo de un CRC es el resto de dividir el valor por un polinomio concreto de valor fijo (por lo general denominado ʻel polinomioʼ).

Ahora, dividamos cantidades de sustraciones repetidas:

7192 ______ 72 )517843 504 ---- 138 72 ---- 664 648 ---- 163 144 -- 19

14 Consulta el “Apéndice 1: Polinomios Módulo 2” en el caso de que quieras acceder a una explicación más detallada de lo que significa en realidad Polinomios Modulo 2. No necesitas saberlo para completar la lección, pero no queríamos dejarte sin que supieses por qué usamos dicho término.

15 Cuando añades 22 a 39 sobre un trozo de papel, añades el 2 al 9 y obtienes 11. Escribes 1 bajo el 2 y el 9, luego escribes un 1 más pequeño en alguna parte bajo el 2 y el 3 en la siguiente columna. Este 1 de menor tamaño es el acarreo (el típico “y me llevo una”), y en la matemática con polinomios módulo 2 te limitas a prescindir de él. Por tanto, esta matemática resulta más simple y también muy rápida.

Page 121: Curso REALbasic

En este cálculo, 72 es el divisor, 7192 es el resultado y 517843 es el dividendo.

En este caso no hay un término apropiado para cada uno de los números que restamos a medida que avanzamos con el algoritmo, de modo que denominaremos a dichos números (como el 504 del primer paso) el substractor; y llamaremos al número obtenido después de restar el substractor y bajar el siguiente número como el objetivo.

Utilizaremos el mismo algoritmo para la división módulo 2; sólo necesitamos saber cómo hacer restas módulo 2, y también averiguar cuantas “veces” restar el divisor para obtener el substractor en cada paso.

Comenzaremos observando que estamos trabajando en binario, de modo que la segunda pregunta se reduce a cuando sustraer 0 o 1 veces el divisor; en otras palabras, cuando sustraer el divisor o no hacerlo. Y esto nos lleva a preguntarnos si el dividendo es inferior al objetivo o no.

Comprobamos que la substación de polinomios módulo 2 es lo mismo que la suma sin acarreo, y por tanto es realmente lo mismo que la operación de or exclusivo (o exclusivo). Podemos confirmarlo observando que dado que estamos ignorando el acarreo, cada posición en la suma o en la resta es totalmente independiente de las posiciones adyacentes. De modo que echemos un vistazo a la suma o a la resta de unos y ceros cuando ignoramos los acarreos:

0 + 0 = 0 0 - 0 = 0 1 + 1 = 0 (dado que 1+1=10 en binario, pero desechamos el 1 que “nos llevamos”) 1 - 1 = 0

0 - 1 = 1 (dado que 1+1=0) 1 - 0 = 1 0 + 1 = 1 1 + 0 = 1

De modo que tanto si sumamos como si restamos, dos unos o dos ceros nos dan cero, mientras que un uno y un cero nos dan uno.

Habrás observado que se trata del mismo resultado que obtenemos al aplicar una operación de o exclusivo (consulta el Apéndice 2: Operaciones Booleanas, para obtener más información sobre las operaciones como el o exclusivo).

Regresemos al asunto sobre cómo determinar si un número es mayor que otro o no. Dado que la suma y la resta son lo mismo, todo lo que podemos hacer para números de la misma longitud es comparar su dígitos situados más a la izquierda; si ambos tienen el mismo dígito a la izquierda del todo entonces puedes considerar cualquiera de ellos como el más grande. Nuevamente, esto facilita el cálculo del CRC: si el primer dígito de ambos, tanto el objetivo como el dividendo, son el mismo entonces restamos (hacemos un o exclusivo) del divendo; en el caso de que sean diferentes, no lo hacemos. En realidad, es incluso más sencillo que esto: el polinomio para un CRC siempre empieza con un uno, de modo que sólo tenemos que mirar al siguiente dígito del objetivo; si es un uno, entonces aplicamos un o exclusivo sobre el polinomio; si no lo es, entonces no lo hacemos.

Un Ejemplo

Page 122: Curso REALbasic

Para calcular un CRC, encuentra el número de dígitos en el polinomio y añade un cero menos a los datos. De modo que en el ejemplo que estamos a punto de ver, dado que nuestro polinomio es 10011, y que tiene cinco dígitos, añadiremos cuato ceros (0000). Ahora sólo queda calcular el resto cuando dividas el polinomio, ignorando los acarreos:

1100001010 _______________ 10011 ) 11010110110000 10011,,.,,.... -----,,.,,.... 10011,.,,.... 10011,.,,.... -----,.,,.... 00001.,,.... 00000.,,.... -----.,,.... 00010,,.... 00000,,.... -----,,.... 00101,.... 00000,.... -----,.... 01011.... 00000.... -----.... 10110... 10011... -----... 01010.. 00000.. -----.. 10100. 10011. -----. 01110 00000 ----- 1110

Echemos un vistazo a las primeras operaciones:

• El primer paso consiste en dividir 10011 entre 11010 (observa que sólo necesitamos observar la misma cantidad de dígitos que tiene nuestro polinomio cada vez). Dado que 11010 empieza con un 1, y que nuestro polinomio siempre empieza con un 1, podemos tomar el Polinomio como menor, de modo que lo restamos. El resultado es 01001. Eliminamos el 0 de la izquierda y pasamos al siguiente dígito del dato para obtener 10011, nuestro siguiente objetivo.

• Ahora estamos dividiendo el polinomio entre 10011. Nuevamente, el dígito de la izquierda es un 1, de modo que volvemos a restar el polinomio. Esto nos da como resultado 00000, de modo que cuando nos movemos al siguiente dígito del dato tenemos 00001.

Page 123: Curso REALbasic

• Ahora, el dígito de la izquierda es un 0, de modo que no restamos el polinomio. Aún tenemos 00001, de modo que ahora movemos el siguiente dígito del dato para obtener 00010.

• Lo mismo de nuevo, de modo que obtenemos 00101.

Etcétera. Dado que nuestro polinomio siempre empieza con un 1, el proceso resulta bastante sencillo: si la posición situada en el extremo izquierdo empieza con un 1, hacemos un o exclusivo sobre el polinomio, de lo contrario no.

Acelerándolo

Echa un vistazo a las primeras líneas de nuestro ejemplo:

1100001010 _______________ 10011 ) 11010110110000 10011,,.,,.... -----,,.,,.... 10011,.,,.... 10011,.,,.... -----,.,,.... 00001.,,....

Advierte que aplicar un XOR sobre una serie de números puede realizarse en cualquier oden16. Para calcular a XOR b XOR c, podemos calcular a XOR b y aplicar un XOR sobre el resultado con c, o podemos calcular b XOR c y aplicar a continuación un XOR del resultado con a. Esto es similar a la suma y a la multiplicación, dado que también podemos realizarlas en cualquier orden.

Ahora, considera lo que ocurre a medida que realizamos los XOR sobre cada par de dígitos en el dividendo: para un posible par de dígitos en el dividendo, siempre haremos un XOR con el mismo par de valores. De este modo, si observamos los dos primeros pasos del ejemplo, estarmos aplicando un XOR con:

10011, 10011 ----- 110101

De modo que podemos realizar los dos primeros pasos del anterior cálculo haciendo un XOR con 110101.

Si realizamos un cálculo equivalente con los cuatro posibles valores que puede tener un par de dígitos del dividendo, entonces podremos recorrer el dividendo de dos dígitos en dos dígitos, haciendo XOR con un valor obtenido de la tabla. Este sería el aspecto que tendría dicha tabla:

16 El término técnico de decirlo es: ʻLa operación XOR es conmutativaʼ.

Page 124: Curso REALbasic

Dato XOR con

00 00000000000000000

01 00000010011010011

10 10011000000100110

11 10011010011110101

El sistema de aceleración más común consiste en desarrollar más este concepto construyendo una tabla con 256 elementos y recorrer el dato un byte cada vez.

Un comentario final: tienes que utilizar polinomios muy particulares para obtener buenos resultados. Calcular cuál sería un buen polinomio requiere cierta matemática avanzada en comparación de la que deseamos cubrir aquí. El Institute of Electrical and Electronic Engineers (IEEE; http://www.ieee.org) supone una buena fuente en la cual buscar dicha información. Una búsqueda en Google sobre “crc polinomios ieee” proporcionará un listado de resultados con buenos polinomios que puedes utilizar sobre CRCs de diversas longitudes. El utilizado en el proyecto de ejemplo es un buen polinomio para CRCs de 32 bits y 32 bits supone una buena longitud para la mayoría de los propósitos.

El proyectoEl proyecto incluido implementa un CRC operado mediante una tabla y desarrollado en REALbasic.

El código es suficientemente corto y sencillo.

1 Abre el proyecto CRC y echa un vistazo al código en la Clase CRC. Empieza con el método BuildTable.Crear la tabla es sencillo: sólo has de realizar los cálculos del CRC para todos números binarios del 0 al 255, un bit cada vez.

2 Ahora echa un vistazo al constructor. Observa que creamos la tabla y definimos el valor de inicio del CRC a todos 1.

3 A continuación, echa un vistazo al método Add sobre el que has de pasar una cadena. Consiste en un bucle sobre los bytes de la cadena, aplicando un XOR sobre el valor actual del registro con la entrada de la tabla correspondiente.

4 Por último, observa que la obtención del resultado es una operación separada del cálculo del CRC (mediante el método Value).

Page 125: Curso REALbasic

Al hacerlo de este modo, podemos suministrar los datos a través de una serie de etapas. Podemos seguir llamando a Add con el fragmento de dato, hasta que hayamos leído toda la información. Esto resulta especialmente útil para, por ejemplo, el software de comunicaciones, en el que podemos estar recibiendo la información en una serie de fragmentos en vez de toda al mismo tiempo.

Usar los CRC

Recuerda lo que hacen los CRC: nos proporcionan una firma para un fragmento de datos. Los usos para dicha capacidad están limitados sólo por tu imaginación. Un par de ejemplos:

• Puedes utilizar un CRC sobre un archivo para garantizar que no haya sido modificado (quizá el archivo de preferencias del programa);

• Puedes calcular un CRC para un documento y encriptarlo a continuación. Si utilizas un sistema de encriptación de clave pública, encontes alguien podría verificar que un documento proviene de ti (dado que podría desencriptar la clave encriptada y obtener un CRC válido para el documento). Si no sabes en qué consiste la encriptación con clave pública, puedes leer sobre ello en Internet. Es una forma de encriptación realmente sorprendente y útil.

Ejercicios AdicionalesEn este punto existen una serie de ejercicios adicionales ciertamente interesantes:

• Amplia el ejemplo para que utilice una tabla de 65536 elementos, de modo que podamos duplicar la velocidad del cálculo.

• Modifica el ejemplo de modo que sólo necesitemos calcular la tabla una vez (particularmente útil en el el caso del la tabla con 65536 elementos). Probablemente esté involucrada la creación de un objeto de tabla o propiedad de Módulo, y pasarlo a la calculadora de CRC.

• Sobrecarga el operador Add para que acepte otros tipos de datos: un array de strings, un número; un array de números.

Page 126: Curso REALbasic

Lección 23 Ejemplo de Calculadora RPN

Objetivo

Hemos terminado con todo el material difícil; a continuación echaremos un vistazo a algunos ejemplos. Vamos a crear una calculadora simple en Notación Inversa Polaca (RPN; Reverse Polish Notation). Si alguna vez has usado una calculadora científica de Hewlett-Packard, entonces habrás usado RPN.

Además de consistir en una buena práctica, este proyecto te ayudará a pensar sobre cómo implementar y usar una estructura de datos de pila (stack, en inglés). También introduciremos el paradigma de diseño de software Modelo-Vista-Controlador.

Modelo-Vista-ControladorGran parte de este curso ha sido diseñado para ayudarte a comprender lo que hacen los objetos y las características del lenguaje REALbasic, además de cómo combinar ambos elementos en programas sofisticados.

En este proyecto avancermos desde el nivel de detalle que hemos estado manejando hasta ahora, a uno más amplio que nos permita observar el modo de estructurar todo un programa: el paradigma de Modelo-Vista-Controlador (MVC).

Antes de que veamos el MVC en mayor detalle, deberíamos considerar otro tema que ha surgido a lo largo de este curso: la eficiencia. MVC puede resultar algo excesivo para muchos programas.

Recuerda lo que hemos dicho con anterioridad: en muchas situaciones, deberías estar escribiendo programas rápidos, simples, y “malos” sólo con el propósito de solucionar un problema lo más rápido posible.

Pero antes de diseñar un programa más significativo, el paradigma MVC parece ser una vía útil para estructurarlo. Si estás diseñando un programa que se encuentre en un punto intermedio, entonces también puedes emplear una estructura a medio camino del MVC.

Sobre los detalles, MVC es un concepto muy generalizado, aplicable prácticamente a cualquier tipo de programa. Este indica que tus programas deberían de encontrarse divididos en objetos que asuman tres papeles bien diferenciados:

• Los objetos del Modelo constituyen los datos que ha de manejar tu programa. En un programa de procesamiento de textos, esto se correspondería con el texto y el estilo, junto con otra información de formato adicional que indique el contenido del documento. En un juego de ordenador, esto podría corresponderse con la ubicación del “mundo del juego”, así como las acciones de cada uno de los “actores” del juego, la puntuación del jugador, etc.

• La Vista es una clase o conjunto de clases encargadas de presentar el modelo al usuario en la pantalla; y

• El Controlador es una clase o conjunto de clases que pueden manipular el Modelo.

Mediante la separación de cada uno de estos tres papeles obtenemos la capacidad de modificar cualquiera de ellos incidiendo mínimamente en el resto de los componentes. Por

Page 127: Curso REALbasic

ejemplo, aumamos que creamos un programa de textos utilizando MVC. Asumamos ahora que queremos hacer que el programa sea multiusuario y permita que dos o más usuarios trabajen sobre el mismo documento a través de Internet. Prácticamente el único cambio que deberíamos escribir sería sobre un nuevo conjunto de clases controlador. El modelo y la vista permanecerían prácticamente sin cambios.

Un aspecto clave: una única clase puede realizar el papel de dos o incluso las tres partes del paradigma MVC. Tal y como veremos, las interfaces nos permiten separar los papeles dentro del objeto. Esto facilitará el hecho de separar cada uno de los tres papeles en varios objetos posteriormente.

La estructura de datos PilaUna pila es una estructura de datos con, como mínimo, las siguientes operaciones:

• Push, y que toma un solo argumento;• Pop, una función que devuelve un único resultado del mismo tipo utilizado por el

argumento Push; y• Empty, una función que devuelve un Booleano.

Estas operaciones sobre la pila funcionan de un moodo equivalente a una pila de platos en una cafetería. Los elementos abandonan la pila en el orden opuesto a cómo se apilaron. En las situaciones en las que no puedes controlar la pila desde el lenguaje de programación, o incluso en el caso de que puedas, es probable que quieras disponer de más operaciones sobre dicho elemento. Algunas de las otras operaciones comunes que puedes desear son:

• Drop, que elimina el elemento que ocupa la parte superior de la pila;• Duplicate, y que añade una copia adicional del elemento situado en la parte superior de

la pila.• Over, y que se encarga de poner el elemento situado el segundo por arriba en la primera

posición de la pila (parte superior);• Swap, y que se encarga de intercambiar la primera y segunda posiciones

correspondientes a la parte superior de la pila;• Size, y que devuelve el contador de los elementos de la pila, y• Clear, encargada de vaciar la pila.

Notación Polaca InversaYa hemos visto con anterioridad que cualquier expresión matemática puede representarse como un árbol sintáctico. Y hemos visto cómo un proceso recursivo (en el que se usa una pila) nos ayuda a recorrer dicho árbol.

Combina ambas observaciones y obtendrás una Notación Polaca Inversa (RPN). En las RPN las operaciones se escriben después de sus operandos, en vez de entre ellos. Un operando es como un argumento en el caso de una función: es la información que debes aportar a la operación. En la expresión

2 + 3

el “+” es la operación, mientras que el “2” y el “3” son los operandos.

Para interpretar una expresión RPN, sólo tienes que leerla de izquierda a derecha; los números se introducen en una pila, mientras que las operaciones cogen sus argumentos

Page 128: Curso REALbasic

de la pila devolviendo a dicha pila el resultado obtenido. Observa lo sencilla que resulta dicha regla de evaluación en comparación con las reglas involucradas en las expresiones aritméticas normales. No hay reglas de precedencia que precisen que debas avanzar o retroceder para evaluar la expresión y tampoco existe la necesidad de emplear paréntesis.

Una observación rápida: hablamos de la pila como los platos en una cafetería: los primeros se encuentran en la base, mientras que los últimos ocupan la parte superior. Pero la convención en la industria informática consiste en ilustrar una pila en un papel como una lista, empezando la parte inferior de la pila en la parte superior de la página. Esto es simplemente una cuestión de acostumbrarse a ello.

Algunos ejemplos de expresiones en notación normal y RPN:

Notación normal RPN

2+3 2 3 +

(2+3)*4 2 3 + 4 *

2+3*4 2 3 4 * +

Recuerda, en la expresión 2+3*4, teniendo en cuenta las convenciones de la aritmética, realizamos la multiplicación en primer lugar, de modo que el resultado es 14. Sin embargo en la versión RPN de esta expresión existe un * antes del +, indicando por tanto el orden en el que deben realizarse los cálculos.

Veamos cómo evaluamos las dos últimas expresiones. En primer lugar, 2 3 + 4*:

Secuencia de evaluación de la expresión

Pila

2 3 23

+ 5

4 54

* 20

Ahora, 2 3 4 * +:

Secuencia de evaluación de la expresión

Pila

2 3 4 234

Page 129: Curso REALbasic

* 212

+ 14

El ProyectoAbre el proyecto SimpleRPNCalculator. Ejecútalo y realiza algunos cálculos.

Las clases y las InterfacesSe han agrupado los elementos del programa según su papel en el paradigma MVC.

La Clase StackDisplay y el Interface StackObserva en primer lugar que sólo tenemos una clase View: la clase StackDisplay. Pero esta clase soporta un interface, Stack, y que forma parte del Modelo.

El interface Stack define el tipo de operaciones de pila que hemos visto anteriormente.

El interface StackEntryEl otro interface del Modelo que se ha definido es StackEntry. Este interface ha sido creado de modo que podemos tratar la caja de entrada como parte de la pila. Si el usuario ha escrito un número pero no ha pulsado Enter, no trataremos la información introducida como el elemento superior de la pila. Los métodos de este interface permiten que la pila pueda comprobar si hay información en la caja de entrada, e introducirlo en el caso de que haya algo. La pila puede ocuparse por tanto de tratar este objeto adicional como parte de sí misma, de modo que las otras partes de la aplicación no necesitan preocuparse de este aspecto.

Utilizamos un interface para esto de modo que el objeto de la pila no tenga que estar conectado directamente con ningún objeto de entrada en concreto. La pila no necesita saber nada sobre cuál es el objeto de entrada (de datos), sólo que soporta las operaciones correspondientes a la interface de StackEntry.

La clase StackOperationButtonLa clase StackOperationButton es una clase abstracta, encargada de añadir soporte para la interface StackOPeration al BevelButton estándar (y que estamos usando como un botón independiente, sólo que tiene más el aspecto de un botón de una calculadora en comparación con el de un botón estándar).

La interface StackOperationLa interface StackOperation se encarga de definir un objeto que sabe cómo manipular la pila; y eso sólo significa que puede almacenar una referencia al objeto Stack cuando se le pide.

La clase NumEditHemos tomado la clase NumEdit de una proyecto anterior. ¡Reutilización del código!

La clase StackEntryBoxEsta añada las interfaces StackEntry y StackOperation a la clase NumEdit. Observa el modo en el que se declaran múltiples interfaces para una única clase.

CalcWindow

Page 130: Curso REALbasic

Manejadores de Evento de Objeto en una ventanaEn este proyecto hemos hecho algo que no hemos realizado anteriormente: hemos puesto sustanciales cantidades de código en los manejadores de evento correspondientes a los objetos de la ventana.

Sólo deberías hacer esto con el código que sea específico para el funcionamiento de la ventana, o del que tengas la absoluta certeza que no necesitarás utilizar fuera de la ventana.

En este proyecto, necesitábamos una amplia variedad de botones, cada uno de ellos con un comportamiento diferente. Hemos incluido tanto comportamiento como es razonable en una definición de clase compartida, y el resto en el código correspondiente a cada uno de los manejadores de evento para cada botón de la ventana.

Observa que cada vez que añadimos código al manejador de evento de un objeto en una ventana, estamos creando de hecho una nueva subclase (sin nombre) sobre la clase de dicho objeto.

El manejador de evento Open en CalcWindowEl Manejador de Evento Open en CalcWindow hace algo que no hemos hecho anteriormente: utiliza el operador IsA para comprobar de forma específica el tipo de cada objeto de la ventana.

Deberías sopesar cada vez que te sientas inclinado a usar el opeardor IsA. Siempre deberías de preguntarte a ti mismo si el polimorfismo podría haber realizado el trabajo correctamente.

En este caso, de hecho, podría haberlo hecho; cada objeto de la ventana podría soporta la interface StackOperation, y los objetos que no son objetos StackOperation del diseño actual podrían haber dejado vacíos los métodos para las StackOperations.

Pero esto habría significado añadir una interface extra a la clase StackDisplay que no pertenece aquí, y eso habría confundido la autodocumentación del diseño. También habría añadido complejidad sin obtener un beneficio evidente. El código de inicialización de la ventana mantiene todo este material en un único sitio, y es realmente sencillo de comprender y de mantener.

Por cierto, esta situación, en el que tienes cierto tipo de colección y necesitas hacer algo sobre algún objeto determinado de la misma, es una clásica situación de “no hay respuesta correcta” en la programación orientada a objetos. Utilizando IsA para encontrar los objetos que quieres resulta con frecuencia (aunque no siempre) una buena situación frente a dicha situación.

Ejercicios AdicionalesEste es un gran proyecto para su ampliación:

• Añade más botones, para más funciones científicas o de negocios.• Añade atajos de teclado. En vez de requerir su configuración en una ubicación

centralizada, las teclas en la ventana deberían de permitir el registro de sus propios atajos de teclado Esto nos premitiría retener una importante característica del programa: puede añadirse una nueva tecla, sin añadir código en ninguna parte salvo en la propia tecla. AsyncKeyeDown (búscalo en la documentación) es un modo de interceptar la

Page 131: Curso REALbasic

pulsación de teclas. Una cuestión interesante sobre el diseño es cuánto pondrías de dicha característica en una definición de clase. Otro aspecto interesante consiste en saber cómo gestionar dos o más botones que solicitan el mismo atajo de teclado; no existe un único modo obvio de resolverlo.

• Añadir modificadores de teclado. Debería de poder obtener Sin-1 manteniendo pulsada la tecla Mayúscula móvil y haciendo clic sobre el botón Sin. Del mismo modo, debería de poder escibir el código específico de cada operación “alternativa” en los botones propiamente dichos.

• Una buena prueba de tu comprensión sobre el paradigma MVC consistiría en separar la pila de la visualización. Es equivalente a decir que la pila debería de ser una propiedad independiente de la ventana, separada de la vista. Exsiten múltiples formas de manejar la interfaz de usuario y su vista en esta situación: la pila podría enviar simplemente una llamada Changed a la vista, y la vista podría pedir entonces los contenidos a la pila; o la pila podría notificar a la vista de cada evento que tuviese lugar sobre ella. Una opción interesante consiste en hacer que los botones notifiquen a la vista cuando hayan finalizado una operación, así como dejar que la pila notifique a la vista en dicho momento (esto sería lo más eficiente, pero también sería lo más difícil de escribir; nuestras discusiones sobre aspectos relacionados con la eficiencia te dirían que el “código eficiente” no es necesario en este caso, pero este resultaría un ejercicio de programación interesante).

Page 132: Curso REALbasic

Lección 24 Calculadora RPN Programable

Objetivo

Haremos que la calculadora de la última lección sea programable con RBScript. Este es un ejercicio interesante, dado que la capacidad de RBScript para comunicarse con otras partes del mismo programa son inicialmente un tanto limitadas17.

Acerca de RBScript

RBScript es una clase que puede ejecutar programas REALbasic. Dichos programas se cargan en un objeto RBScript como texto, siendo compilados a continuación para su ejecución. Esto significa que puedes hacer que tu programa sea modificable por el usuario final.

Ten en cuenta que actualmente está disponible para RBScript un subconjunto de todo el lenguaje REALbasic. Consulta la Referencia del Lenguaje para obtener más detalles.

En este punto deberías leer toda la información disponible sobre RBScript en la Referencia del Lenguaje.

El retoEl reto en usar RBScript es que sólo ofrece un modo muy simple de comunicarse con el resto del programa. El código que se está ejecutando en un programa RBScript tiene acceso a prácticamente todo el lenguaje de programación REALbasic, pero no a cualquiera de los objetos incorporados en REALbasic. Sólo hay dos comandos que pueda utilizar un programa de RBScript para comunicarse con el resto del programa:

• La función Input y que toma un único argumento de cadena llamando a continuación al manejador de evento Input del objeto RBScript, y que recibe la misma cadena como argumento. El manejador de evento puede devolver una cadena, y que constituye el resultado de la función en RBScript.

• El comando Print y que toma un único argumento de cadena y llama al manejador de evento Print del objeto RBScript, encargado de recibir la cadena como argumento.

RBScript no puede llamar a ninguna parte del programa sobre el que está ejecutándose, o bien utilizar algún otro modo de comunicarse con dicho programa más allá que los dos comandos indicados.

El reto consiste en que queremos proporcionar la capacidad de “añadir código RBScript a un botón” y ampliar las capacidades de RBScript de modo que soporte algunos comandos adicionales para comunicarse con la pila de la calculadora. Implementaremos dos funciones (Pop y Size) y métodos de tamaño (Clear, Drop, Dup, Over, Swap y Push).

17 Esta lección fue escrita cuando RBScript estaba en sus primeras encarnaciones. RBScript es ahora mucho más flexible y potente, y también mucho más sencillo de usar en tus proyectos. Se ha mantenido esta lección inalterada porque es un buen proyecto para ilustrar varias técnicas de programación. Del mismo modo, la mayoría de las técnicas utilizadas aquí aun resultan útiles, con ciertas variaciones, incluso cuando se está usando la última versión de RBScript. Una versión del proyecto que usa una de las nuevas características de RBScript se incluye en RBScript Calculator (Contexto).

Page 133: Curso REALbasic

En este punto deberías detenerte y pensar sobre cómo podrías permitir que el usuario final escriba un script como este:

DupPush Pop * Pop

cuando RBScript no proporciona dichos comandos de pila.

Consejo: tendrás que añadir código adicional al RBScript proporcionado por el usuario, y en el que se incluya la implementación para todos los comandos de la pila. Dichos comandos necesitarán comunicarse con la pila mediante los comandos Print e Input.

El Proyecto• Abre el proyecto RBScriptCalculator.

Lo que haremos será definir un Protocolo para que el código RBScript pueda comunicarse con el resto del programa. Un protocolo es un conjunto de reglas que todo acuerdan en cumplir. En ete caso definiremos un conjunto de mensajes que pueden enviarse mediante los comandos Input y Print en RBScript, así como los los eventos asociados en el objeto RBScript, y que implementará los comandos de manipulación de pila que necesitamos. A continuación añadiremos código adicional al RBScript proporcionado por el usuario, y que implementa los métodos encargados de manejar el protocolo para cada acción. Para el usuario final aparecerá como si dichos métodos formasen parte de RBScript.

1 Abre el editor de código correspondiente a la clase StackScript.2 Examina los manejadores de evento Input y Print.El protocolo es bastante sencillo. Por ejemplo, si recibimos la cadena “Pop” en el RBScript, extraeremos el valor situado en la parte superior de la pila y lo devolveremos.

El comando más completo es Push y que necesita soportar la recepción de un argumento desde el RBScript. Lo que hacemos es separar el valor del comando mediante un espacio.

3 Examina ahora el código llamado desde el constructor en esta clase.Para simplificar la adición de métodos adicionales al RBScript crearemos una cadena mediante algunos métodos auxiliares. Comenzamos advirtiendo que sólo hay tres tipos de funciones o métodos que precisamos añadir a la cadena que estamos almacenando en TheScriptHeader, y que será incorporada al código RBScript del usuario:

• Métodos sin argumentos:• Métodos con un argumento; y• Funciones sin argumentos

Observamos que todos ellos hacen lo mismo: pasan su nombre mediante los comandos Input o Print. En el caso de que haya un argumento, este se pasa mediante el mismo comando, separado del nombre con un espacio.

De modo que creamos tres métodos auxiliares para gener declaraciones de método o función, teniendo en cuenta el nombre del método o de la función, y entonces llamamos al método auxiliar correspondiente para cada método o función que tengamos que añadir al TheScriptHeader. Observar que tenemos una AddFunction1 que no usamos nunca; sólo

Page 134: Curso REALbasic

está ahí por cuestiones de completitud, en el caso de que queramos ampliar este proyecto en algún momento dado.

Aparte del material indicado, se encuentran métodos getter y setter para el script propiamente dicho, un método SetStack (de modo que esta clase pueda sera una StackOperation) y eso eso es todo.

4 Revisa RBScriptButton y ButtonEditWindow para ver cómo funciona todo esto.

5 Ejecuta el proyecto y haz Alt + clic (Opción + clic en el Macintosh) sobre uno de los botones vacios y añade un script en él. Por ejemplo:

DupPush Pop * Pop

Ejercicios adicionalesEste es un gran proyecto para su ampliación:

• No existe ninguna gestión de errores, lo que hace que resulte muy sencillo conseguir que el programa provoca una excepción no gestionada (unhandled exception). Añade la gestión de errores suficiente para que no haya modo de que el usuario pueda provocar una excepción no gestionada. Probablemente debieras ignorar algunos errores, y mostrar el comando más útil que puedas sobre el resto. Ten en cuenta que el objeto RBScript tiene una propiedad para indicarte qué línea se está ejecutando. Sería buena idea indicar al usuario qué línea de RBScript está causando un error.

• Añade comandos adicionales que puedan ser empleados por el usuario. Por ejemplo, serían útiles comandos para leer y escribir datos desde archivos; y

• Un ejercicio más ambicioso sería el de que la calculadora guardase los scripts en sus botones cuando se salga de la aplicación, volviendo a cargarlos la próxima vez que se ejecute. La función PreferencesFolder es muy útil para esto...

Page 135: Curso REALbasic

Lección 25 Gráficos I

Objetivo

Vamos a trabajar con material divertido relacionado con los sprites. En consonancia con las técnicas de programación que hemos visto, vamos a comenzar con una pequeña pieza y probarla. Esta pieza es una imagen que puede apuntar en diferentes direcciones.

Este también supone un ejemplo excelente de por qué deberías de haber prestado más atención durante las clases de trigonometría en el colegio...

Vamos a crear una jerarquía de clase para genera y mostrar un gráfico que pueda apuntar en diferentes direcciones. Dado que vamos a utilizarla posteriormente, queremos ser capaces de pasar las coordenadas x e y, relativas al centro del objeto, y permitirle que apunte hacia dichas coordenadas.

Esto implica usar algo de trigonometría. Pero no te asustes, te explicaremos la trigonometría necesaria a medida que la usemos.

Pre-lecturaDeberías leer sobre los objetos Canvas y Graphics en la Guía del Usuario antes de continuar.

El Proyecto

1 Abre el proyecto incluidos, DrawChevron, ejecútalo y observa lo que hace.

2 Examina el código que reside en la clase DirectedChevron, comenzando en la parte superior de la jerarquía:

• DirectedPicture es una clase abstracta que permite su reutilización;• BufferedPicture es un tipo de clase abstracta que proporciona un framework de una

solución básica para este problema: en vez de generar una imagen que apunte con precisión hacia el punto deseado, tenemos una cantidad de imágenes pregeneradas, (por lo general 8 o 16, identificado por loa propiedad NumPics) y mostramos aquella que apunte lo más cerca de las coordenadas en las que estemos interesados.

• DirectedChevron nos da algunas imágenes reales que pueda mostrar BufferedPicture.

PíxelesEn la pantalla de ordenador una imagen se reduce en última instancia a una rejilla de pequeños cuadrados o celdas coloreadas. Cada una de estas celdas se denomina pixel (abreviatura de picture element — elemento de imagen).

CoordenadasTradicionalmente, una posición en una imagen bidimensional de un ordenador se mide desde la parte superior izquierda de la imagen. Un punto se describe mediante dos números, y que representan la distancia hacia la derecha y hacia abajo de este punto. Dichas distancias son denominadas generalmente como las coordenadas x e y, respectivamente. Como abreviatura, en ocasiones escribiremos la posición de un punto (x,y). De modo que la posición inicial superior izquierda sería (0,0) y un punto que se encuentre 100 puntos hacia la derecha y 50 unidades hacia abajo se escribiría como (100,50).

Page 136: Curso REALbasic

La trigonometríaTrigonometría significa “el estudio de los triángulos”. Las funciones trigonométricas relacionan los ángulos de un triángulo equilátero con la longitud de sus lados.

Algo que debes saber sobre todas estas funciones en REALbasic: los ángulos se miden en radianes, por varias razones matemáticas en las que no entrareremos por ahora. Existen 2 * Pi radianes en un círculo completo. Esto sólo significa que donde te veas tentado de utilizar “360” (dado que piensas en grados), debes escribir “2 * Pi”, y que donde te veas tentado de usar “180” escribas “Pi”. (También querrás definir una constante Pi, tal y como tenemos en este proyecto).

DirectedChevron.GenerateFrameEl papel de DirectedChevron es el de proporcionar las suficientes imágenes para crear un giro de un cuarto. La superclase se encargará de crear la simetría de dichas imágenes para crear las suficientes imágenes que completen un giro total. Situaremos los puntos del chevron en torno a un círculo que quepa justo dentro de ThePicture.

Comenzaremos calculando el radio de dicho círculo. Este es de la mitad del ancho de ThePicture:

Figura 62. El Chevron dentro de ThePicture.

(captura Figura 62)

A continuación, calculamos cuantos radianes rotaremos cada imagen en comparación con la última:

AngleInc = 2 * Pi * (n-1)/NumPics // cuánto vamos a rotar

Figura 63. AngleInc radianes incrementados para cada imagen.

(Captura Figura 63)

AncleInc Radianes en torno al círculo

A continuación, calculamos donde residirá cada punto en el círculo. Cada punto estará representado por su distancia x (horizontal) e y (vertical) desde la esquina superior izquierda del ThePicture.

La parte frontal del chevron está en AngleInc radiantes en torno a las 12 en punto. Necesitamos un modo de convertir un ángulo en sus componentes x e y.

Trigonometría básica

Ahora tenemos que hallar dónde se encontrarán las tres puntas del chevron. Para ello necesitamos convertir un ángulo y una distancia en coordenadas x e y.

Asumamos por el momento que el centro de nuestro chevron está en (0,0). Queremos un punto rotado p radianes, y r píxeles desde (0,0). Esto nos permite dibujar un triángulo:

Figura 64. La tarea consiste en hallar a y b en este triángulo.

Page 137: Curso REALbasic

(Captura Figura 64)

Unidades a

Unidades b

Unidades r

p radianes

Nuestro punto de partida es el punto inferior del triángulo, y el punto que queremos hallar es el punto situado a la derecha en el triángulo. Podemos dibujar dicho punto puesto que yendo r píxeles a un ángulo determinado de p radianes desde la vertical es lo mismo que subir b píxeles y moverse a continuación a píxeles a la derecha. Observa que ir recto hacia arriba y después recto hacia la derecha significa que ambos movimientos se producen en ángulo recto. Dos funciones trigonométricas, Sin y Cos nos permiten hallar a y b:

• El Sin de p nos indica el valor de a/r. Dado que conocemos el valor de r, y queremos saber el de p, entonces resulta que a es igual a r * Sin(p).

• De igual modo, el Cos de p es b/r, de modo que b es igual a r * Cos(p).

En general estas funciones pueden utilizarse en cualquier triángulo con un ángulo recto en la arista. La cara opuesta del ángulo recto se denomina hipotenusa. El Sin (seno) de uno de los ángulos que no se corresponde con un ángulo recto es el ratio de la cara opuesta de dicho ángulo con la hipotenusa; el Cos (coseno) del ángulo es el ratio de la cara adyacente con la hipotenusa. También se encuentra la función Tan, y que se corresponde con el ratio de la cada opuesta con respecto a la cada adyacente.

Puedes recordar todo esto con el acrónimo (en inglés) SOCHAHTOA (Sin: Opuesto/Hipotenusa; Cos: Adyacente/Hipotenusa; Tan: Opuesto/Adyacente).

Ahora queremos centrarlo no en (0,0) sino en el medio de la imagen en la posición (r,r). De modo que sumaremos r a cada valor x e y. También definimos una constante SideAngle, y que indica la distancia desde la parte superior del chevron hasta su cara opuesta en la que situamos los puntos posteriores del chevron. Esto nos proporciona el siguiente código para dibujar el chevron:

Sub GenerateFrame(n As Integer, g As Graphics) //Genera un chevron apuntanto al punto “n” en un total de UBoundPics + 1//Pre: El ancho y alto de la imagen son idénticos//0<=n<=UBoundPics //UBoundPics >= 6, UBoundPics es par //Post: p contiene el chevronDim r As Integer Dim AngleInc As Double Dim Points(8) As Integer Const PI=3.14159265358979323846264338327950 Const SideAngle = 0.8 //Proporción de media vuelta sobre la parte posterior del chevronr = ThePicture.width/2

Page 138: Curso REALbasic

AngleInc = 2*Pi*(n-1)/NumPics //How far around we are to rotate //Punto de chevron Points(1) = r + r * sin(AngleInc) Points(2) = r + r * cos(AngleInc) //Cara lateral Points(3) = r + r * sin(Pi*SideAngle + AngleInc) Points(4) = r + r * cos(Pi*SideAngle + AngleInc) //Centro Points(5) = r Points(6) = r //Cara izquierda Points(7) = r + r * Sin(Pi*(-SideAngle) + AngleInc) Points(8) = r + r * Cos(Pi*(-SideAngle) + AngleInc) //Dibújalo g.ForeColor = DarkBevelColor g.ClearRect 0, 0 ,g.Width,g.Width g.FillPolygon(Points)

Como siempre, deberías buscar cualquiera de los comando de REALbasic con los que no estés familiarizado. Sin embargo, y resumiendo, FillPolygon toma un array de enteros, trata cada par de números como un punto y dibuja un polígono (la figura) entre dichos puntos, rellenándolo con el color que esté definido en ForeColor.

¿Qué imagen apunta aquí?El otro método para el que necesitamos la trigonometría es el encargado de seleccionar la imágenes que debe dibujar para que apunte a una posición concreta.

Lo que necesitamos para esto es la función inversa de Tan mencionada anteriormente: en esta ocasión, tenemos la posición y queremos hallar el ángulo. La parte de la ubicación dividida por la parte x de la coordenada se corresponde con la Tan (tangente) del ángulo, tal y como se ha mencionado anteriormente.

Existe una función Atan que calcula el ángulo a partir de este ratio pero, de hecho, REALbasic cuenta con una función de uso más sencillo: Atan2 toma un valor x e y, y devuelve el ángulo desde (0,0) para dicho punto. De modo que sólo tenemos que restar r de todo para regresar a (0,0) y usar Atan2. A continuación sólo hemos de redondear al ángulo más cercano al que apuntan nuestras imágenes (¿recuerdas que AngleInc = 2 * Pi * (n-1) / NimPics) en GenerateFrame? Sólo hemos de averigüar cuantos de hay en nuestro ángulo). Esto nos proporciona el siguiente método:

Sub FaceToward(X As Integer, Y As Integer) Dim NextPic As Integer Const PI=3.14159265358979323846264338327950 NextPic = ((Round(atan2(X, Y)*NumPics/(2*Pi)+ NumPics) mod NumPics)) + 1 If NextPic <> CurrentPic Then CurrentPic = NextPic ThePicture.DrawPicture ThePics(CurrentPic), 0, 0 End If

Ahora deberías echar un vistazo al proyecto y ejecutarlo, e indagar en él hasta que hayas averiguado como funciona.

Page 139: Curso REALbasic

Ejercicios AdicionalesExisten múiltples posibilidades de ampliar este proyecto, y deberías intentar poner en práctica tus propias ideas. Algunas interesantes podrían ser:

• Crea la clásica aplicación de “par de ojos que miran al cursor”, con círculos para las pupilas. Como bonificación extra podrías ampliar la aplicación para que siga al apuntador dentro incluso de los ojos propiamente dichos (lo que significa que si el puntero se encuentra dentro de uno de los ojos la pupila estará centrada bajo el puntero).

• Dibuja una forma más compleja (por ejemplo un tanque o un robot) en vez de una punta de flecha, o bien

• Crea una clase que pueda iniciar a partir de dos imágenes bitmap (una que apunte hacia arriba, y otra en 45 grados), generando a continuación las 8 imágenes restantes como fruto de rotar cada una de las dos imágenes originales en tres ángulos rectos.

Page 140: Curso REALbasic

Lección 26 Comunicaciones

ObjetivoDesarrollaremos una aplicación sencilla aunque útil para demostrar el uso de los puertos TCP. La aplicación envía un mensaje de texto a otro ordenador que está ejecutando la misma aplicación.

TCP/IPDel mismo modo que hemos visto los protocolos en otras situaciones, existen diferentes protocolos para que los ordenadores puedan comunicarse en una red. El protocolo que usaremos es el TCP/IP.

Este es el principal protocolo de Internet. Al igual que con la calculadora, la implementación subyacente del protocolo se encarga de ocultar la complejidad para el software que lo utilice.

Para enviar información a un ordenador a través de Internet18, debes saber dos cosas:

• La Dirección IP correspondiente al ordenador sobre el que se está ejecutando el programa, y

• El Número de Puerto en el que está escuchando el ordenador con el que estés intentando comunicarte.

Ya has visto una Dirección IP anteriormente. Dichas direcciones están formadas por cuatro números entre 0 y 255 con puntos en entre ellos (como esta: 210.18.155.12). Contiene toda la información necesaria para localizar un ordenador en Internet.

Un número de puerto es un entero entre 0 y 6553519. Es parecido al buzón de una dirección convencional, y es así porque las conexiones a un ordenador emplean differentes números de puerto de modo que puedes tener múltiples programas ejecutándose en un mismo ordenador y comunicándose con varios servidores en Internet, todos ellos al mismo tiempo. Cada paquete de información enviado entre dos ordenadores contiene una especificación para el número de puerto en el que debe recibirse.

Pero, ¿cómo hacen los dos programas que se están comunicando entre sí para saber qué puertos han de utilizar? Existen tres posibles modos:

• Existen ciertos estándares aceptados y que se utilizan para determinados servicios. Por ejemplo, los servidores Web utilizan por lo general el puerto 80. De modo que cuando escribes www.realbasic.com en tu navegador web dicha dirección se traduce en una dirección IP20, enviando a continuación un mensaje Hypertext Transport Protocol al puerto 80 en dicha dirección.

18 Usando el protocolo para las comunicacines en internet comunmente más utilizado, TCP (Transmision Control Protocol).

19 O puede ser también 65537. Después de varias horas buscando en Internet el escritor no ha podido asegurarse por completo.

20 Utilizando para ello otro servicio estándar sobre dicho número de puerto, denominado Domain Name Service. Es probable que hayas tenido que introducir direcciones de servidor DNS al configurar la conexión a Internet, y que indican qué ordenadores se encargan de realizar dicha traducción de direcciones por ti.

Page 141: Curso REALbasic

• Cuando un programa recibe una conexión con otro equipo suele solicitar al sistema opertivo un número de puerto aleatorio que aun no se haya utilizado, y pide al programa del otro extremo que responda en dicho número de puerto.

• Las direcciones de Internet (denominadas técnicamente Universal Resource Locator o URL; Localizador de Recursos Universal) puede especificar el número de puerto después del nombre del DNS o la dirección IP del servidor (separado de la dirección con dos puntos). Sería posible para REAL Software ejecutar un segundo programa servidor en el mismo ordenador utilziado para el servidor por omisión, y enlazar con él desde el puerto por omisión 80 utilizando una dirección normal; algo así http://www.realsoftware.com:8080.

La información enviada a través de Internet está dividia en pequeños paquetes y pueden recibirse desordenados, o bien requerir la retransmisión de alguno de estos paquetes debido a algún error, pero todo esto es lo que sucede en una capa inferior proporcionada por TCP21. El servicio TCP garantiza que el mensaje llegará intacto y que las partes que lo componen llegarán ordenados. Sin embargo, la información no tien por qué llegar necesariamente al mismo tiempo. Necesitamos tener esto presente cuando escribamos software de red.

Con esto en mente, echemos un vistazo al proyecto que vamos a crear: éste envía un mensaje de texto al mismo programa que funciona en otro ordenador. ¡Este será un programa realmente útil!

El proyecto1 Detente y piensa por un momento cómo sería un protocolo adecuado22 para este proyecto.El único problema real a cubrir consiste en cómo el programa receptor puede saber que ha llegado el mensaje completo.

2 Examina el código del proyecto incluído: MessageSender.Deberías leer en la referencia del lenguaje sobre el objeto TCPSocket. Advierte que todo lo que debemos de hacer es: creamos un socket receptor y le indicamos que escuche (Listen). Y si queremos enviar un mensaje, pedimos al socket de envío que envíe.

3 Examina durante unos minutos el RFC 821 incluido, en el que se describe uno de los protocolos más importantes de Internet: el SMTP.

Se denomina Request for Comment porque así es como empiezan estos estándares: alguien publica una idea y pide a la gente que la comente; está publicado por la Internet

21 Un servicio de comunicaciones Internet está compuesto por varias capas. La capa TCP es la que proporciona el envío fiable de paquetes en orden utilizando para elo una capa inferior denominada UDP (Universal Datagram Protocol) en la que no se proporciona ninguna de estas garantías. Esto es importante para algunos propósitos, la velocidad es más importante que la corrección (transmitiendo audio o vídeo, o reproduciendo un juego en tiempo real, por ejemplo), de modo que algunos programas necesitarán usar UDP, dado que resulta más rápido.

22 Para asegurarte que toda esta charla sobre los protocolos es clara en un punto: es común construir protocolos sobre otros protocolos. Un nivel inferior en los protocolos de comunicaciones en Internet sólo se encargan de enviar los paquetes a donde deben ir. Otro protocolo se asienta ʻencimaʼ de este para corregir los errores de transmisión producidos en el nivel inferior, y para unir mensajes largos a partir de los paquetes del nivel inferior, reuniéndolos en orden (esto nos lleva al TCP). Utilizaremos este nivel para implementar un protocolo de alto nivel inventado por nosotros para el envío de mensajes.

Page 142: Curso REALbasic

Engineering Taskforce (IETF), quien define la mayoría de los estándares (en su mayor parte protocolos) en los que está basado el funcionamiento de Internet.

Es muy importante que los protocolos para enviar y recibir email y páginas web, y la mayoría de los tipos de información transportados por Internet, estén disponibles para cualquiera que quiera comprenderlos e implementarlos. el IETF crea todos estos RFC definiendo cómo funcionan dichos servicios estándar y hace que estén disponibles de forma gratuita. Si quieres escribir un servidor web, un navegador web, un servidor de correo, o un programa que funcione con cualquiera del resto de estándares de Internet, eres libre de hacerlo.

4 Exmina la sección de Puerto en la Referencia del Lenguaje.

5 Céntrate en los numerosos posibles errores que pueden ocurrir cuando se comunican en red dos ordenadores independientes. No hemos intentado gestionar este tipo de errores para mantener el ejemplo lo más simple posible.

La a mayoría del trabajo en la implementación de programas que se comunican por Internet consiste por lo general en gestionar todos estos errores.

Ejercicios adicionalesEl proyecto puede ampliarse de varias formas:

• Gestiona el envío de texto con estilo (define la propiedad Styled del TextField a True, y envía tanto el texto como la propiedad correspondiente a TextStyleData);

• Permite arrastrar y soltar cosas (archivos, recortes) sobre la ventana y también desde la ventana del otro ordenador.

Cualquiera de estas ampliaciones en el proyecto requerirán, por supuesto, que amplíes el protocolo.

Page 143: Curso REALbasic

Lección 27 SImulación y Animación

Objetivo

Vamos a llevar a cabo un ejercicio razonablemente significativo de simulación y animación. Produciremos un SpriteSurface con una colección de las flechas creadas en una lección anterior, y las pondremos apuntando en la dirección del cursor. A continuación veremos algunas de las ventajas de la orientación de objetos cuando tratemos lo sencillo que resulta animar los sprites.

Ten en cuenta que nuevamente habrá algo de trigonometría relacionada con este proyecto.

Lectura previa

Antes de proseguir deberías leer sobre las clases correspondientes a SpriteSurface y Sprite.

Simulación de física

En un juego es bastante habitual diseñar una simulación física. Por lo general no llegarán a ser totalmente realistas, pero haremos algo que se asemeje a lo que podría ser el comportamiento de los objetos en el mundo real.

En este caso, tendremos un grupo de objetos con el mismo comportamiento. En particular estos objetos tendrán:

• Momentum, lo que significa que tendrán una dirección y velocidad de movimiento, y que requerirán la aplicación de una fuerza o fuerza para cambiar dicha dirección ny velocidad; y

• Empuje, que es la fuerza ejercida en la dirección a la que estén apuntando.

La combinación de ambas produce objetos cuyo comportamiento será similar al de los cohetes pero desplazándose sobre hielo. Otro modo de decirlo es que estamos simuilando una situación sin fricción y sin gravedad.

Tendremos los objetos apuntando en todo momento hacia el apuntador del ratón.

¿Puedes imaginar su aspecto?

Las matemáticas

Necesitamos registrar las posiciones y momentum de los objetos, y aplicar empuje sobre ellos.

La mayoría de la matemática relacionada hace uso de los vectores. Un vector es, a grandes rasgos, una flecha: es una dirección y una distancia23 o (de forma equivalente) una distancia x e y.

23 Se suele utilizar el término “magnitud” en vez de “distancia” dado que un vector puede representar no sólo una dirección y distancia sino también otras cosas que tenga dirección y “cantidad”; como por ejemplo una dirección y una cantidad de empuje.

Page 144: Curso REALbasic

También proporcionamos otras tres operaciones sobre los vectores (veremos cuán útiles son a medida que exploremos el proyecto):

MultiplyByProporcionaremos una operación para multiplicar un vector por un Double. Esto multiplica la magnitud por el Double (número de coma flotante). Puesto que vamos a almacenar el vector como una cantidad de x e y, tenemos que escalar ambos, utilizando para ello una trigonometría muy parecida a los cálculos realizados en la lección de Gráficos I.

NormalizedEsta función devuelve un nuevo vector con la misma dirección que el vector sobre la que se aplique, pero con una Magnitud de 1.

Add y Operator_AddPara comprender la suma de dos vectores, imagínalos como flechas y pon la segunda flecha al final de la primera24:

Figura 65. c = a + b.

(captura Figura 65)

Para calcular la suma de dos vectores podemos añadir simplemente los componentes horizontales de los dos vectores para obtener el componente horizontal del resultado, y los componentes verticales de los vectores para obtener el componente vertical del resultado:

Figura 66. Para sumar vectores, suma por separado los componentes vertical y horizontal.

(captura Figura 66)

El primer proyecto

1 Abre el primero de los proyectos incluidos, SpriteFlock.

2 Examina el código en la clase Vector; asegúrate de que puedes ver cómo se implementa las operaciones descritas arriba.

Advierte que la mayoría de las operaciones modifican el vector propiamente dicho, pero que la función Add devuelve un nuevo vector.

3 Examina el código en la clase MomentumSprite

Esta utiliza principios de animación simples, moviendo el sprite según su velocidad actual entre los diferentes fotogramas. No queremos arreglar la velocidad, de modo que la velocidad de los sprites variará en función de la rapidez del ordenador sobre el que se esté ejecutando el programa.

24 La función Operator_Add nos permite definir nuestro propio operador + para esta clase. En otras palabras, nos permite halla la suma de dos vectores v1 y v2 escribiendo v1 + v2. Ten en cuenta que no hemos utilizado hasta ahora esta capacidad en ninguno de nuestros proyectos.

Page 145: Curso REALbasic

Ten en cuenta que si bien MomentumSprite es un sprite, y por tanto tiene propiedades X e Y enteras, necesitamos mantener las coordenadas del sprite como valores de tipo Double, para evitar errores de redondeo (lo que provocaría que el sprite “saltase” en velocidades bajas, por ejemplo). Realizamos todos los cálculos sobre la posición utilizando los valores dobles y redondeando el resultao hacia el entero más cercano con el objeto de obtener las propiedades X e Y de cada fotograma.

Esto también significa que creamos una operación MoveTo, de modo que cuando movemos el sprite hacia alguna posición recordemos ajustar tanto la X e Y y ExactX y ExactY a la nueva posición. Este es un buen ejemplo de por qué ajustar directamente las propiedades es menos flexible en comparación con el uso de métodos get y set.

A continuación, observa que el momentum del sprite se mantiene como un vector, y que tenemos una operación Apply, para añadir aceleración al momentum actual. Para cada fotograma averiguaremos la dirección en función de la posición del ratón, y aplicaremos un vector en dicha dirección para que el vector represente el momentum.

El último método es Incrment y que se encarga de hallar la nueva posición del sprite. Nuevamente, estamos sumando dos vectores (la posición actual es como un vector). No utilizamos un vector para esto porque necesitamos componentes X e Y independientes en este punto.

De modo que la posición es un vector, la velocidad es un vector y la aceleración es un vector. Sumamos la aceleración a la velocidad y la velocidad a la posición25.

4 Echa un rápido vistazo por el resto del programa.

Comentarios adicionales

• Hemos identificado las dos constantes importantes (la cantidad de sprites y el valor de su empuje) en constantes cuyos valores podamos modificar con facilidad o bien convertirlos en variables globales. Casi siempre suele resultar una mala práctica incluir directamente una constante en el código. Esto no significa que en algunos casos no sea lo correcto, pero sí significa que siempre deberías de pensar sobre ello antes de introducir un valor constante (número o cadena) en tu código. Un ejemplo de un valor que está bien incluir como código es PI, dado que es bastante improbable que camibe...

• Un problema del orden en el que se llama a los eventos en la ventana implica que no podamos inicializar todo en el evento Open de la ventana: en ese momento la ventana no está maximizada, de modo que no sabemos dónde hemos de poner los sprites. Existen varias formas de solucionarlo; en este caso hemos utilizado un método simple pero práctico de poner todo en marcha un segundo después de que se haya ejecutado la aplicación, utilizando para ello un temporizador. De ahí la existencia de InitSpriteSurface; y

• Ten en cuenta que de de algún modo hemos separado en esta simulación la visualización del sprite (no lo hemos separado de forma agresiva, tal y como hemos comentado anteriormente, porque queremos mantener el código simple). Esto será importante para nuestro siguiente proyecto, donde veremos lo sencillo que resulta animar los sprites como resultado.

25 Si tienes conocimientos básicos de cálculo, entonces tenemos la posición, la primera derivada de la posición (la velocidad) y la segunda derivada de la posición (la aceleración).

Page 146: Curso REALbasic

5 Ejecuta el proyecto. ¡Rápido!Detente y considera cuan fácil podría adaptarse esa simulación a un amplio rango de cosas interesantes: por ejemplo el simulador de un sistema solar.

Observa lo juntos que empiezan los sprites pero a lo largo del tiempo se van alejando más y más.

AnimatedSpriteFlock

1 Abre el proyecto AnimatedSpriteFlock. Ejecútalo.

Advierte la diferencia: los sprites están animados (cambian de color).

Para ello creamos una clase que sustituye la fuente gráfica del anterior código. Esta nueva clase (AnimatedPicture) contiene uno o más objetos DirectedPicture. En función de la cantidad de tiempo transcurrida, esta pasará una petición de imágenes en bucle a los objetos que contiene. Por tanto, esta clase contenedora proporciona un recorrido cíclico de imágenes basado en el tiempo.

2 Examina el código encargado de llevarlo a cabo¡Observa lo simple que es!

Además de algún código encargado de la inicialización y un constructor adicional y un méotdo en la clase DirectorChevron (de modo que podamos obtener el color desde otra instancia para definir el colo de su opuesto), los cambios para añadir esta nueva capacidad son realmente directos.

¿Puedes observar como esto es posible gracias a un buen diseño orientado a objetos? La respuesta involucra el uso de polimorfismo y una simplifcación del paradigma modelo-vista-controlador. ¿Puedes observar hasta qué punto es un tipo simplificado del diseño MVC?

Corregir la velocidadAdvierte que si bien los ciclos de animación están basados en tiempo, la velocidad a la que funciona la simulación depende de la cantidad de sprites y de la velocidad del ordenador (esto determina el framerate del SpriteSurface).

Hay algunos modos de solucionar esto: ya sea ajustando de forma explícita el framerate del SpriteSurface; separando por completo el modelo y actualizándolo mediante el uso de un temporizador; o dejar que el modelo se encargue de registrar el tiempo transcurrido entre fotogramas, y ejecutar la cantidad de pasos de simulación necesarios en función de la cantidad de tiempo que haya transcurrido. Piensa sobre las ventajas y desventajas de cada uno de ellos.

Ejercicios adicionalesEste proyecto puede ampliarse de varias formas. Algunas ideas para empezar:

• Incorpora algunas físicas adicionales (puedes hacer algo de investigación en Internet o en libros de texto sobre físicas para obtener información adicional). Algo razonablemente fácil consiste en añadir fricción. Más complicado es conseguir que los sprites reboten entre sí (la opción fácil: asumir que cada uno es en realdiad un círculo). Este es un buen

Page 147: Curso REALbasic

punto de partida para pensar sobre físicas realistas, frente a algo que tenga un aspecto razonable.

• Crea un simulador del sistema solar. Ya sea uno basado en un sistema solar real, o bien tener un sol central sobre el que estén atraidos el resto de los objetos, y sitúa unos cuantos objetos con masas aleatorias, posiciones y velocidades y observa como van todos hacia el sol, giran en el espacio o entran en una órbita estable. Resulta razonablemente fácil hacer que el sol sea simplemente una fuente de gravedad; y resulta interesante crear un proyecto donde cada uno de los objetos tenga su propia gravedad. Calcular dicha propiedad tiene que ver con la realización de una serie de operaciones proporcionales a N2 para N objetos, de modo que no llevará mucho tiempo para que unos pocos objetos empiecen a ralentizar la simulación.

• Crea un juego o juguete como por ejemplo una simulación de patinaje sobre hielo. Tendrás que investigar sobre otras capacidades del SpriteSurface como por ejemplo la detección de colisiones o el escaneado del teclado, pero la investigación siempre es algo bueno...