Story Transcript
computLuz. ecabrera. abril 2010
Computar Calcular es obtener un resultado asumiendo la verdad de ciertas premisas y definiciones, aplicando un procedimiento preestablecido sobre datos identificados. En este sentido, toda la matemática es cálculo, aunque se llame así a una rama particular de ella. Todo empieza con la necesidad de obtener, o justificar su validez si ya se le tiene, de los resultados, por lo que establecer las premisas y definiciones, identificar los datos y crear un método que conecte inescapablemente los datos con los resultados es un proceso inverso. Hay cálculos informales, que se suelen realizar mentalmente, para los que basta una mera aproximación. Y algo similar ocurre con aquellos que básicamente implican el conteo, registro y operaciones aritméticas básicas, como sumar, restar, multiplicar y dividir. Cuando el cálculo numérico se vuelve tedioso porque requiere el procesamiento de mucha data, si bien las operaciones numéricas sean sencillas y rutinarias, se suelen usar asistentes mecánicos clásicos, como ábacos, registradoras y calculadoras electrónicas simples. Cuando los cálculos muy complejos se usaron las reglas logarítmicas (de cálculo), las tablas o cartas trigonométricas, logarítmicas y de otras especializaciones. En todo caso, el ser humano interviene en casa etapa del proceso de cálculo. Computar es el calcular sin intervención humana, si la hay, sólo en la entrada de datos. Y llamamos computadoras a estos asistentes artificiales que tan esencial ayuda brindan a la humanidad en su necesidad creciente de realizar cálculos cada vez más sofisticados, complejos y precisos. Llamamos computadoras a los mecanismos artificiales capaces de realizar computación de manera autónoma porque constan con la capacidad de adquirir los datos, almacenarlos en su memoria interna, donde también reside el algoritmo para procesarlos, y tienen la forma de presentar los resultados. Las computadoras, u ordenadores, como prefieren llamarlos en algunos países de Europa, no sólo liberan al hombre cálculos sumamente tediosos, sino que permite realizarlos en una fracción relativamente muy pequeña fracción de tiempo, sino realizar cálculos imposibles de otra manera. Las computadoras permean ya toda la vida y cultura contemporáneas, al punto de ser casi imposible concebirlas sin ellas, y más aún, imaginar un futuro sin ellas. Tal es la ubicuidad de esta magnífica creación del ingenio colectivo de la humanidad. Las computadoras han tocado casi todas las aéreas de la actividad humana, al punto de volverse casi imprescindibles, inimaginable cualquier futuro sin ellas y sus extensiones inevitables, los robots, que no requieren humanos en ninguna de las etapas del cálculo. Conocer, pues, algo de las computadoras es imprescindible para el hombre culto de la actualidad. Y no me refiero a utilizar las maquinas en si, sino algo del trasfondo sobre el que ha emergido este portento de la creatividad humana, que, como otras maquinas, puede hacernos bien o mal, dependiendo de nosotros.
computLuz. ecabrera. abril 2010
Ordenador
Que yo sepa, al menos los franceses y los españoles prefieren llamar ordenadores a lo que nosotros en América llamamos computadoras. Al perecer, ellos notaron desde el principio que estas maquinas hacían algo o mucho mas que “triturar números”, calcular, o procesar. Algo que nos permite darles la razón de inmediato es notar que para lograr que un proceso manual pueda ser automatizado introduciendo computadoras se requiere una completa clarificación, explicitación, en detalle, de todo el proceso manual, imponiendo, por decirlo así, un orden. Para los no iniciados en informática, esta explicitación de los procesos manuales para poderlos automatizar con éxito resulta en una imposición que puede lucir hasta arbitraria, especialmente cuando ya ha sido “enlatada” en otra parte, implicando una adopción al paquete ya pensado. La computadora impone un orden a los procesos, al menos aquellas de que disponemos hasta ahora. Pero eso no es culpa de la computadora, sino del mundo en que vivimos, que es demasiado complejo y aleatorio para la capacidad intrínseca de cálculo del individuo humano promedio. Por la misma razón las matemáticas sufren de tan mala reputación hasta en buena parte de los ingenieros, que es mucho decir. Para poder calcular, hay que imponer un orden, porque calcular implica alinear las conclusiones con las premisas de manera inescapable. Y esto requiere disciplina mental y física. Naturalmente, es muy difícil luchar contra la cultura establecida, y no pienso hacerlo aquí, al menos no frontalmente, por lo que en lo adelante seguiré llamando computadoras a lo que creo más correcto llamar ordenadores, pues entonces algunos podrían perderse innecesariamente en nuestro discurso. Pero hay muy grandes beneficios prácticos del orden que las computadoras nos han ayudado a establecer, como la eliminación de los grandes archivos de documentos físicos y cambiarlos por bases de datos y de documentos digitales, y la música en discos de pasta a discos “digitales”. Ni que decir de todas las películas en celuloide convertidas a discos compactos, con la posibilidad abierta por la mayor densidad de laser azul, ‘BluRay”, al cine 3D hogareño, la descarga de música y “películas” desde servidores de Internet o P2P, a la PC o el móvil y hacer “streaming’. Todo esto está indisolublemente ligado a la computadora, porque los “hard disks” (magnéticos), “compact Disks” (ópticos”) y los “pen drives” o “stack memories” (estado sólido) comparten un matematización común, llamada bits, que es una versión moderna de la lógica booleana, de dos valores y sus algoritmos correspondientes. La más estricta lógica matemática rige todo este mundo “digital”, que la “amigabilidad” (“friendlyness”) de las aplicaciones de ventanas (form applications) y los “hojeadores” (browsers) han logrado transparentar tan eficientemente al “usuario final”, de manera que todo mundo, como debe ser, tiene acceso. Por tanto, el orden que las computadoras los han traído ha sido bueno, y en gran manera, pues nos ha simplificado enormemente la vida y ha hecho integrar la data, el procesamiento y la visualización en toda clase de artilugios (gadgets) que han llevado el concepto del entretenimiento a cotas inimaginables hace pocas décadas.
computLuz. ecabrera. abril 2010
Bits Un bit, se dice, es la menor unidad posible de información, porque representa una de dos alternativas, verdadero/falso, encendido/apagado, si/no, aprueba/suspende, masculino/femenino, vivo/muerto, y cualesquiera otras situaciones que decide reducir a sólo dos opciones. Dije menor unidad de información porque lo dije en un sentido amplio, porque un bit tanto puede representar tanto información (resultados o productos del procesamiento) como datos (materia prima del procesamiento) porque ambos, a fin de cuentas, requieren el mismo formato de representación. En español usamos 29 letras para escribir cualquier palabra de nuestro idioma, incluyendo, por supuesto, las 26 letras del alfabeto inglés y las tres que algunos ya ni saben que son letras, a saber, la ch, la ll y la ñ. Para escribir cualquier número usamos (aparte del . decimal y el signo -) 10 símbolos, a saber, 0,1, 2, 3, 4, 5, 6,7, 8, 9. Para redactar, transmitir y recibir un mensaje se requiere de un alfabeto, un código fundamental último que le de concreción o realidad material al mensaje, y esto con independencia del contenido, simple o complejo, del mensaje mismo. En español tenemos las tetras del abecedario, los guarismos (dígitos), los signos de puntuación, entre otros. Los primeros computadores electrónicos usaban un “alfabeto” numérico, literalmente, de 10 dígitos (valga la redundancia, porque dígito implica que son 10), lo cual se explica porque sus creadores eran humanos y estaban apenas dando los primeros pasos en esta área y usando lo que tenían a mano. Para representar un dígito en una computadora como el ENIAC usaban unos tubos al vacío (bulbos electrónicos) que tenían 10 niveles de corriente de placa, uno por cada dígito del sistema numérico decimal. Pero bien pronto a alguien se le ocurrió que se podía simplificar enormemente el diseño y la confiabilidad usando sólo dos estados. En efecto, los dígitos se pueden traducir a binario como 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000, 1001, usando cuatro bits, o “dígitos” binarios para traducir cada “dígito” decimal. Bajo este concepto los Laboratorios Bell, en EE.UU crearon una computadora completamente funcional con sólo relés, simulando ceros y unos binarios. Con la emergencia del transistor, un dispositivo binario por naturaleza, se logró simplificar el diseño, incrementar la confiabilidad y el poder de las computadoras, que por entonces, dicho sea de paso, se usaban exclusivamente para calcular, ya fuera trayectorias de proyectiles, descifrar código criptográfico enemigo, o casas por el estilo. También el tamaño de los equipos se redujo en varios órdenes de magnitud, así como el consumo de energía, y se hicieron comercialmente asequibles, con lo cual salieron del mundo científico y entraron en nuestras vidas para quedarse. Un teléfono “inteligente” de hoy tiene miles de veces el poder de un computador de primera generación. Las letras del alfabeto inglés y todos los caracteres fundamentales se codificaron en una tabla, llamada ASCII (American Code for Information Interchange), de manera que todas las computadoras de entonces pudieran “entenderse”, pues se asoció a cada carácter una secuencia de ocho bits, llamada byte u octeto. Cuando pulso la tecla A en mi teclado, en realidad se genera el código ASCII correspondiente, que es un 65 en binario, a saber 01000001. El mismo código se usa para almacenarlo en la memoria interna del computador y para trasmitirlo a la pantalla o al modem para que viaje por la Web. El ASCII se extendió a 16 bits, llamado Unicode.
computLuz. ecabrera. abril 2010
Programar Programar es el arte de redactar algoritmos para que los ejecute una computadora binaria. Digo arte porque aunque tenga una base científica, como la música o el dibujo, se requiere de la creatividad del autor para plasmar de una manera concreta lo que está abierto a múltiples posibilidades. Digo algoritmo para referirme a un método detallado, paso a paso, finito e inambiguo, que soluciona una tarea específica, como calcular un área plana o estimar la probabilidad de un suceso. Digo que es para que lo ejecute una computadora, sin asistencia humana directa. Aprender la esencia de la programación de computadoras requiere de algunos meses y no del uso de computadoras, si bien es recomendable para acelerar el proceso mediante la retroalimentación. Programar con destreza requiere de varios años de práctica continua, pero una vez logrado, resulta muy satisfactorio. Así como cualquier dato o información puede ser reducido a bits, cualquier cosa que una computadora hace se puede reducir a un conjunto relativamente pequeño de microinstrucciones, que ya vienen preestablecidas en el microprocesador. La incompatibilidad entre procesadores viene dada por este hecho. Así como los humanos no manejamos con facilidad directamente lo escrito en binario, tampoco lo hacemos con las microinstrucciones, que están también en binario, por supuesto. Pero afortunadamente existen los llamados lenguajes de alto nivel, más próximos al nivel de entendimiento del humano promedio. Se ha desarrollado una amplísima variedad de lenguajes a lo largo de estos casi 70 años de desarrollo informático, por lo que hay mucho de donde escoger. Hay para todos los gustos y colores. Hay algunos que se consideran obsoletos, y los hay de vanguardia. Los hay monopolizados por un “fabricante” exclusivo, y los hay abiertos a comunidades planetarias. Los hay para principiantes y para hackers, para el nivel de máquina y los hay descriptivos para inteligencia artificial. Hasta para niños los hay, así como para dispositivos específicos como impresoras. Una gran ventaja de programar es que es muy parecido a hacer matemática: Se puede hacer con “lápiz y papel”, o en términos más contemporáneos, con un simple editor de texto. Y asistimos a una de las épocas que más facilidades ofrecen al que quiera aventurarse en este fascinante mundo. Como país, tener un número apreciable de programadores nos podría hacer mucho más competitivo, pues la necesidad de desarrolladores de software se acentúa cada día y los países punteros en tecnología están subcontratando cada vez más la redacción de código de computación a países en desarrollo, como el nuestro. Es recomendable que todos los alumnos, al menos del primero del bachillerato en adelante, sean expuestos al aprendizaje de la programación en algún lenguaje de alto nivel porque esto, como la aritmética, el álgebra y la geometría, les ayuda a desarrollar su capacidad de resolver problemas en general y en particular para la ciencia y la tecnología. Entiendo que elevar significativamente el nivel de la programación en nuestro país es más importante que el del turismo, las zonas francas y la inversión extranjera, porque es algo que se incorpora a nuestra población y nos permitiría desarrollar productos muy atractivos para el mundo desarrollado.
computLuz. ecabrera. abril 2010
Data
Desde el punto de vista del programador en un lenguaje de alto nivel, los datos o valores se dividen dos grandes categorías: Constantes y variables. Las constantes son valores fijos, como el números 7 y -1.7, los caracteres ‘A’ y ‘\n’, y las cadenas de caracteres “Hola” y “Adiós”. Se guardan en un lugar transparente de la memoria. Las variables son lugares de la memoria con nombres asignados a los que se pueden asignar distintos valores concretos a medida que el programa se ejecuta, se acceden en la memoria mediante su nombre simbólico y que pertenecen a una cierta categoría o tipo, como número entero, número real, o cadena de caracteres. Hay miles de tipos de datos y se pueden crear a la medida, derivándolos de los pres-existentes, creando nuevos conglomerados para aplicaciones particulares. En última instancia la computadora los almacena en binario, pero eso es transparente al programador en un lenguaje de alto nivel. Los valores enteros sirven para representar multitud de magnitudes u objetos del mundo real, como la edad de una persona, el día el mes y el año de una persona, la hora en hora, minutos y segundos, los números naturales (para determinar si son primos o perfectos, por ejemplo). Los valores reales sirven para representar longitudes, temperaturas, volúmenes, dinero, probabilidades y pare usted de contar. Los valores reales, distinto a los enteros, que tiene solamente parte entera, tienen una parte entera y una parte decimal. A la computadora le da más trabajo manipular números reales que números enteros. Las cadenas de caracteres, o valores alfanuméricos, se usan para representar nombres de personas y lugares, teléfonos, correos electrónicos, direcciones, y cosas por el estilo. No se pueden realizar cálculos directamente con valores alfanuméricos, pero se pueden convertir si fuere necesario y posible. Los valores lógicos o booleanos se utilizan para representar una de dos alternativas, como masculino/femenino, citadino/rural, nacional/extranjero, local/internacional, vivo/muerto, true/false. En realidad, hay un tercer valor posible para los booleanos, que se debe hacer explícito en caso de uso, el nulo (null). En efecto, si en un formulario que un usuario llena por Internet hay una pregunta con dos opciones y éste decide dejarla sin contestar, el valor nulo significa en este caso “no contestada”. Este concepto se extiende a los enteros, reales, cadenas de caracteres y otros. Por ejemplo, el nombre de una persona podría ser pedro y lo representaríamos con la cadena de caracteres “pedro”, pero antes de asignarle el valor pedro la cadena podría estar nula, lo cual podríamos indicar con “”, dos pares de comillas vacías, que representan una cadena nula. También existe el tipo de dato carácter, que representa un único carácter, como ‘A’ o ‘’, el primero representa la primera letra del alfabeto español, en mayúsculas (ASCII = 65), mientras que la segunda se refiere al carácter nulo. Note que los caracteres individuales se encierran entre comillas simples, no dobles. Se puede decir que las valores carácter (simple) son extensiones de los booleanos en el sentido de que en vez de únicamente las alternativas true/false (sin considerar el null), admiten múltiples opciones, como todas las letras del alfabeto, los guarismos, los signos de puntuación, y hasta caracteres chinos y japoneses, como ocurre de hecho.
computLuz. ecabrera. abril 2010
Leer
Una operación elemental que se requiere realizar normalmente en un programa de redactado en un lenguaje de alto nivel es la llamada lectura de datos, mediante la cual la computadora adquiere, desde el mundo real, proporcionados por el usuario, los datos que necesita para realizar su proceso o algoritmo. Normalmente los datos se leen en forma de cadenas de caracteres y se convierten al tipo de dato específico que realmente representan en la memoria de la computadora, como números enteros o reales, si fuere el caso. Naturalmente, no se requiere conversión si se van a manejar internamente como caracteres o cadenas de caracteres. Se asume que al especificar la sentencia o comando de lectura, se activa el “scanneo” del teclado (normalmente tres veces por segundo) y el usuario teclea el dato, pulsando la tecla “Enter” o “Intro” para indicar que ya concluyó la entrada del dato, esto así porque hasta pulsar “Enter’, puede borrar múltiples veces si se equivoca o cambia de idea. Para poder alojar en la memoria un valor dado por el usuario, leído, se asume que previamente el programador ha declarado una variable para alojar el dato, de acuerdo al tipo decidido a manejar en la memoria, que si no es alfanumérico requiere conversión al tipo correspondiente. Una variante de leer un dato es que el programador declare la variable del tipo adecuado y le asigne directamente un valor constante. A esto se llama inicialización y suele hacerse en la etapa de prueba de un programa para verificar el comportamiento del programa antes de ponerlo a interactuar con el usuario. Aparte de ser etapa provisional a la lectura, la inicialización tiene su propio espacio para asignarle un primer valor a una variable contador (usada para contar y con ello controlar un proceso o registrar las veces que se repite) o un acumulador (usada para acumular sistemáticamente valores que se generan por un proceso o que proporciona el usuario). Hay una importantísima variante de la lectura en que participa el usuario final del programa y es cuando una función (también llamada método, procedimiento, subrutina o subalgoritmo) recibe valores en los parámetros con que se comunica internamente con el programa que la contiene. En este caso se dice que el programa (en realidad su función principal) pasa valores a la función o método invocado y que aquella acepta o recibe los valores que le son enviados. Esta es una auténtica lectura, especialmente si se toma en cuenta que una función pretende resolver un problema de manera muy general. En efecto, si redactamos un programa que determine si un número es perfecto (igual a la suma de sus factores propios, como lo son 6 = 1+2+3+4+6 y 28 = 1+2+4+7+14) y lo pruebo inicializando “manualmente” el valor a 6, es muy distinto a si le pido al usuario que proporcione a probar si es perfecto. Es claro que en este segundo caso, hemos dado un importante salto de abstracción, pues el programa ya no es para un valor específico, sino que toma el valor dado por el usuario, cualquier valor en amplísimo rango de valores, y mediante un ciclo local lo procesa para determinar si corresponde a un número perfecto o no. Pero el paso es aún más gigantesco si mi programa toma el valor dado por el usuario y se lo pasa a una función que toma el valor, lo procesa y retorna a mi programa el valor lógico correspondiente, true, si el número es perfecto, o false, si el número no es perfecto.
computLuz. ecabrera. abril 2010
Escribir Uso la palabra escribir para resumir todo lo que corresponda a una salida del programa (o función) al exterior, sea que se despliegue en la pantalla (en una consola ventana de fondo negro con letras blancas, o en una forma gráfica con etiquetas, cajas de texto y botones), se imprima en papel, se envié por módem, o se retorne de una función. Escribir implica que se envía un mensaje (como instrucciones al usuario para que sepa qué dato introducir) o mostrar un resultado fruto de algún proceso, ya sea de un programa o una función. Obviamente, esta es la función más importante realizada por un programa porque de no dar resultados, para nada se procesaron los datos. Como ocurre con la lectura, normalmente los programas sólo pueden enviar cadenas de caracteres a la salida, por lo que es típico que los resultados tengan que convertirse a cadenas de caracteres antes de poder ser visualizados por el usuario. Pero esto no supone un inconveniente mayor. A veces hay que concatenar resultados para presentarlos en forma más accesible para el usuario, y a veces hay que darles un formato especial, como redondear los números reales o indicar de forma especial que son negativos, o insertar caracteres especiales o espacios, o letreros. Claro, hay formas muy sofisticadas de presentar resultados de un programa, tanto que hay aplicaciones de software dedicados especialmente a elaborar, procesar y presentar consultas y reportes, a veces llamadas reporteadores. Son de uso muy intensivo en un campo llamado aplicaciones de bases de datos. La mayoría de los lenguajes de programación de alto nivel disponen de un conjunto de sentencias especiales y cláusulas (opciones dentro de una sentencia o comando) para presentar los datos al usuario de manera adecuada, legible y amena. Esto incluye la posibilidad de mostrar gráficos en pantalla e impresos. Una variedad sumamente importante de dar salida a los resultados de un programa es grabándolos en un dispositivo de almacenamiento externo, como un disco duro, en forma de archivos de datos, que a su vez toman muy variadas formas. La salida en disco tiene ciertas ventajas sobre los datos mostrados en pantalla, pues se puede realizar una sola vez para cada conjunto de datos, pudiendo recoger los resultados sin tener que volver a leer nuevamente los datos desde el usuario. Y lo mismo podemos decir de los datos si los almacenamos en un archivo. Almacenar los resultados en un archivo puede significar un enorme ahorro de papel, especialmente si luego, o directamente, se convierte a un formato digital portable (PDF), de extrema importancia en un mundo cada vez más consciente de su entorno ecológico. La forma de salida de archivos más simple son los archivos de texto, como los que genera un editor de texto plano, que desde el punto de vista del usuario equivalen cadenas de texto almacenadas en un medio permanente. Pero la salida puede darse en muy variados formatos una vez se conoce la forma de accederlos. Lectura y escritura se complementan. La primera suele darse al inicio del programa y la última al final, mas hay no siempre es así porque en ocasiones un programa necesita darle instrucciones al usuario para introducir datos, o pedir retroalimentación para determinar la rama de procesamiento a seguir.
computLuz. ecabrera. abril 2010
Secuencial Los programas más simples y triviales sólo constan de sentencias o comandos secuenciales, esto es, declarar variables, inicializar variables, leer valores, asignar valores mediante puras fórmulas lineales, y dar escribir resultados o instrucciones para el usuario. Sin embargo, puede sorprender la cantidad enorme de problemas cotidianos, de tecnología, ingeniería, matemática y ciencia que encajan en esta categoría, como calcular áreas de figuras planas, conversiones entre sistemas de magnitudes, por citar sólo un par. Pero aunque un programa secuencial pueda ser trivial, lo es al compararlo con programas más sofisticados, y conviene asegurarse bien de quemar bien esta etapa en el proceso de formación de formación de un programador neófito, pues, a fin de cuentas, los programas más complejos se descomponen en piezas pequeñas. En efecto, puesto que el ser individuo humano típico suele tener una memoria corta (cada vez más por el uso intensivo de dispositivos que “recuerdan” todo por nosotros), conviene mantener cada “ladrillo de construcción” de un programa complejo tan pequeño como sea posible. Y esto muy a menudo implica que muchas de las funciones que constituyen un programa son puramente secuenciales en su interior, si bien, conceptualmente son miembros de una colectividad que en conjunto no es nada secuencial, y además esta pieza secuencial resuelve un problema particular de manera general. Lo que recomiendo es que el tema de descomponer los programas que admiten una solución puramente secuencial, trivial, se amplíe descomponer el programa en dos partes, una que declara las variables, realiza las inicializaciones que apliquen, se lean los valores necesarios. Pero en lugar de realizar los cálculos localmente, redactados directamente en la función principal del programa (Main), que se invoquen las funciones a las cuales se envíen los valores que requiera para obtener los resultados necesarios, asignado a variables locales lo retornado por las funciones. Por ejemplo, si un programa va convertir de temperaturas Celsius a Fahrenheit, en lugar de aplicar directamente la fórmula de conversión en el programa invocar un método (función) al cual se le envíe el valor dado por el usuario para la temperatura Celsius y que retorne la temperatura convertida a Fahrenheit. Esto enfatiza desde temprano el hábito, muy saludable por cierto, de encapsular los detalles de programación en funciones que los manejan de manera general y reutilizable en programas futuros. De esta manera, la mentalidad ‘divide y vencerás’ echa raíces bien pronto en el programador principiante, con las ventajas que implica. Así, el tema de redacción de funciones que reciben uno o múltiples valores, mientras tanto con paso por valor, se introduce en las primeras horas del entrenamiento, de tal suerte que se puede seguir reiterando con numerosos ejemplos hasta afianzarlo hondamente en la práctica del nuevo programador. Cuando más adelante se trate el tema de las funciones o subrutinas, ahora haciendo paso por referencia, ya el alumno no tiene que aprender tantas cosas juntas sobre las funciones, y todo ello es compatible con el uso a que ve obligado el alumno con las funciones intrínsecas, para leer y escribir, por ejemplo.
computLuz. ecabrera. abril 2010
Parámetros Una función o subrutina (también procedimiento, subtarea, subalgoritmo) es un “ladrillo de construcción” de programas que se redacta expresamente para ser invocada por otras funciones, incluyendo la función principal (Main) de un programa, de manera que se le asigna la realización de una tarea específica de manera general. El principio detrás de la redacción de funciones en lugar de largos programas, “longanizas”, como le llamamos algunos, es “divide y vencerás”, o el llamado método cartesiano, según el cual la solución de un problema complejo se simplifica si se descompone en la solución de varios problemas menos complejos constituyentes. La idea fundamental al redactar una función es determinar sus parámetros, esto es, qué retorna y qué recibe, cuál es el resultado que debe dar y qué datos necesita para obtenerlos, de manera que todo los pasos, sean muchos o pocos, para obtener el resultado a partir de los quedan encapsulados en el cuerpo de la función. Esto también se puede ilustrar como “no hagas hoy lo que puedes hacer mañana”, que parece un muy mal consejo en la práctica cotidiana, pero es oro de 14 quilates en el contexto de la programación de aplicaciones complejas, como las que demanda la realidad de hoy. Y no sólo los pasos o el código quedan encapsulados al interior de la función, sino los datos intermedios que localmente sean necesarios para conseguir los resultados a partir de los datos explícitamente suministrados a la función. Esto puede llevar a una reducción notable de la cantidad de variables a manejar en un momento dado. Más aún, los nombres usados para los parámetros de la función pueden ser los mismos p pueden ser otros, sin que esto cree problemas, puesto que cada función existe como una entidad separada, por lo que la coincidencia de nombres es sólo eso, coincidencia, sin efectos colaterales. Eso significa que se puede usar el mismo nombre para variables que corresponden al mismo concepto o a conceptos diferentes sin que haya confusión, puesto que el contexto cambia en cada función. Lo único que hay que mantener es que los tipos sean compatibles cuando se requiera. Por ejemplo, puedo enviar la variable entera num, desde la función Main, por ejemplo, a una función como parámetro y recibirla en la función con el parámetro n, sin problemas, siempre que declare a n como entera, como hice con num en la función principal que invoca. Las variables que se declaran localmente (aparte de los parámetros) no son visibles desde fuera de la función. Más aún, invocar una función desde otra equivale a reducir la visibilidad de varias, incluso de muchas líneas (si esa función invoca a su vez a otra u otras) a una sola línea donde está el nombre de la función y sus parámetros. Me resulta casi imposible exagerar los beneficios que se derivan de esta estrategia de programación, pues por sí sola puede no sólo hacer posible abordar problemas complejos con entera confianza, sino que vuelve casi trivial (con la práctica) la solución de problemas de mediana dificultad, que de otra manera “freezan” a muchos principiantes. La estrategia de descomponer un problema en varias funciones o subrutinas también ha sido descrita como “onion pilling” (pelar la cebolla), haciendo alusión a la metáfora según la cual al ir abordando el problema por capas, a menudo ocurre que se llega a la última capa sin encontrar nada difícil de resolver, como al pelar una cebolla.
computLuz. ecabrera. abril 2010
Decisión Los problemas del mundo real, que modelamos en computadoras para ayudarnos a resolverlos, suelen ir más allá de lo puramente secuencial o lineal, e implican distintas líneas de procesamiento dependiendo del conjunto de datos de entrada o de condiciones que se dan durante el procesamiento mismo. Por ello, los lenguajes de programación de alto nivel tienen sentencias para abordar la toma de decisiones, o bifurcaciones de flujo, como también se les llama. Siempre se trata de evaluar una proposición lógica para determinar si es verdadera (true) o falsa (false). A veces se realiza alguna acción simple, o un conjunto de ellas, cuando la proposición resulte verdadera, y ninguna acción se lleve a cabo en caso de ser falsa, parecido a salirse de la carretera si suena el celular, para contestarlo, pero seguir manejando normalmente si no suena. Pero también hay provisión para realizar una o más acciones si la condición resulta verdadera y otra u otras si resulta falsa, por lo que siempre se hará algo, luego de evaluar la proposición, sea verdadera o sea falsa: Se hará algo si es verdadera y otra cosa si es falsa. Note que se asume que el flujo de las acciones ocurren en el código en el mismo sentido en que leemos (y escribimos) un texto en español: De arriba abajo, línea por línea, y de izquierda a derecha, en cada línea. Esto es tan obvio que se me olvida explicitarlo. Esto significa que un programa secuencial puro se ejecuta “por gravedad”, de arriba hacia abajo y sin saltos, pero un programa con decisiones, se encuentra con “burbujas de aire” que le hacen caer de golpe, de manera similar a los aviones que viajan en medio de terrenos montañosos. También se puede insertar una decisión en la rama verdadera, la falsa, o en ambas, de una decisión superior, de manera que se pueden escalonar o tejer múltiples decisiones, pero no se recomienda encadenar demasiados niveles de decisión, pues el código se puede volver ilegible, y eso es un contrasentido si se usa un lenguaje de alto nivel. También hay decisiones escalonadas naturalmente, de manera que se evalúa el valor de una variable (entera o carácter), una sola vez, y dependiendo del valor que tome, se ejecuta la rama correspondiente al valor especificado. Para los valores no incluidos se puede se puede incluir el “caso por omisión” que los engloba a todos. Note que un programa que sólo incluye sentencias secuenciales y decisiones siempre, aunque brinque, siempre lo hace hacia abajo, nunca hacia arriba, porque las decisiones lo más que pueden hacer es impedir que un fragmento de código no se ejecute (porque la condición es falsa), pero nunca manda a retroceder a código ya explorado. Personalmente nunca enseño el uso de decisiones escalonadas, pues, en mi opinión, riñen con la estética que debe tener el código y son ampliamente sustituibles, con muchas ventajas, con el uso de estructuras de control y de datos posteriores. Las decisiones son una la forma de incluir la no linealidad del mundo real en nuestro código. Nótese que esto es distinto a como se le rompe invocando funciones, porque allí ocurre de manera incondicional, esto es, se salta de la función que invoca a la invocada, se ejecuta la invocada y se retorna a la sentencia siguiente en la que invoca.
computLuz. ecabrera. abril 2010
Ciclos Un ciclo nos permite indicar que queremos escribir un fragmento de código una sola vez, pero que se ejecute varias veces, incluso muchas veces, hasta muchísimas veces, si fuere necesario, con tal de que sea un número finito de veces. El uso de ciclos es lo que realmente, en mi opinión, justifica el uso de las computadoras. En efecto, los ciclos son los que permiten a la computadora realizar por nosotros esas tediosísimas “trituraciones de números”, con “warp-drive” (hiper-velocidad, en Star Trek) que requieren los proyectos de investigación contemporáneos y futuros, librando a los humanos de tarea tan aburrida. Pero, justo es reconocerlo, las computadoras modernas, capaces ya de realizar miles de millones de microinstrucciones por segundo no sólo nos libran de trabajo tedioso, sino que hacen posible alcanzar la solución de problemas que sin ellas ni podríamos soñar siquiera. Pero, como dijo alguien, decir las cosas es más fácil que hacerlas, a menos que uno sea tartamudo. Y mi experiencia me dice que este es el primer gran escollo que encuentra el programador principiante, los ciclos, pues no es coda trivial encontrar la forma general del código a insertar en un ciclo. El ciclo más utilizado, por mucho, es el que se controla mediante un contador, al cual se le asigna un valor inicial, se le coloca una condición para repetir y el incremento de una variable para que se realice cada vez se ejecute el ciclo. Por ejemplo, para desplegar los números pares positivos menores que diez, es decir, 2, 4, 6, y 8. Una solución trivial, que satisface enteramente este caso particular, es escribir “2 4 6 8” y el caso pasa a la historia. El problema es que ha costado muy poco esfuerzo y por lo mismo es un código desechable, de uso en un sola ocasión, como un envase de foam. Si pienso un poco más en el asunto puede que a pensar en una variable contador, la cual inicializo en 2, le pongo como condición para repetir el ciclo que sea menor que 10 y como incremento 2. Dentro del ciclo sólo tengo que escribir el valor actual de la variable contador, y, como por arte de magia, se escriben los números 2 4 6 8. Una gran obvia ventaja de esta construcción (que no es invención mía, sino que la aprendí hace muchos años ya) es que es fácil extenderla a un montón de otros casos similares, cambiando la inicialización, la condición para repetir y el incremento. Incluso podemos leer los valores de inicio, fin e incremento y, el usuario casi está programando. Pero hay otros ciclos disponibles para cuando no sabemos de antemano cuántas veces se ejecutará el ciclo el ciclo, sino que solamente tenemos, por ejemplo, la condición para repetirlo, y la restricción, digamos, de que se debe ejecutar al menos una vez. Me atrevo a afirmar que con los ciclos empieza el arte de la programación a que aludí unos artículos más atrás, pues ciertamente encontrar qué ciclo es adecuado en cada ocasión y “ajustarlo” a la necesidad en cuestión, entiendo yo, requiere un balance adecuado de creatividad y disciplina. Disciplina porque las estructuras del lenguaje son limitadas porque no hay que reinventar la rueda, y creatividad, porque a pesar del limitado “abecedario” de sentencias, el espacio para dejar la huella personal es realmente ilimitado, créanme. ¿Pero qué pensamiento no se puede expresar con las 29 letras del español? ¿Y en binario?
computLuz. ecabrera. abril 2010
Arreglos
computLuz. ecabrera. abril 2010
Referencia
computLuz. ecabrera. abril 2010
Estructuras
computLuz. ecabrera. abril 2010
Clases
computLuz. ecabrera. abril 2010
Librerías