



Image via Wikipedia


Si este blog fuera lo que alguna vez pretendí que fuera, me gustaría que se pareciera a Word Aligned, y para que tengan una idea de que les estoy hablando, he traducido un artículo de ese extraordinario blog, espero que lo disfruten (incluso los que son menos técnicos). (*)
Detén el reloj, aplasta el bicho [1]
Por Thomas Guest (Word Aligned)
¿Cuál de estos relojes es el mejor?



Podríamos fácilmente argumentar que el que está detenido.
¿Podemos? En "Los Dos Relojes"[2] Lewis Carroll argumenta de otra manera.
¿Qué es mejor, un reloj que da la hora exacta una vez por año, o un reloj que es puntual dos veces por día? "Este último", contestarás, "incuestionablemente". Muy bien, ahora atiende.
Supongamos que tengo dos relojes: uno no funciona en lo absoluto, y el otro se retrasa un minuto al día: ¿cuál preferirías? "El que se retrasa", replicarías sin ninguna duda. Ahora observa: el que se retrasa un minuto al día tiene que emplear doce horas, o setecientos veinte minutos, hasta que de nuevo señale la hora correcta; por consiguiente es puntual una vez cada dos años, mientras que el otro es puntual evidentemente siempre que sea la hora por él indicada, lo que ocurre dos veces al día.[3]
Esta es una distracción divertida, pero no realmente problemática: por supuesto que el reloj que pierde tiempo es de uso más práctico, aún así es paradojal, mientras menos tiempo pierda menos a menudo dice la hora correcta. Un reloj que pierde sólo un segundo al día sólo indica la hora correcta cada 118 años aproximadamente.
Software Bugs
Mencione estos relojes defectuosos porque estoy pensando sobre los fallos en el software (bugs) y como los buscamos y corregimos.


El código que es obviamente correcto es más fácil de detectar que aquel que es casi correcto, y detectar fallos es previo a corregirlos. Esto implica - construyendo sobre la terminología de Carroll - que es improbable que despachemos muchos relojes detenidos, pero si no somos cuidadosos podemos terminar despachando unos pocos que pierden tiempo. Y, en general, el código que es obviamente erróneo es más fácil de corregir que el código que es casi correcto. Una función terriblemente rota claramente necesita un replanteo; mientras que una que casi funciona puede simplemente ser ajustada hasta que parezca funcionar, a menudo resultando en un error más sutil.
Fugas y Carreras
C y C++ proveen buenos ejemplos de lo que estoy hablando. Consideren un programa que usa mal la memoria. Un intento de alojar un espacio de trabajo de 4294967295 bytes falla instantáneamente [4]; una lenta fuga de memoria, como el reloj lento, puede causar un daño no perceptible por un periodo extendido.
Herramientas decentes detectarán fugas de memoria. Condiciones de carrera (race conditions) en un código multi hebras son dificiles de seguir y pueden mostrarse elusivas durante las pruebas del sistema. Más de una vez he dejado un programa corriendo en un depurador (debugger), siendo alimentado con entradas al azar, con la esperanza de de que una rara y aparentemente aleatoria condición gatille una interrupción en la ejecución. ¡Denme código realmente roto cualquier día!
75% correcto vs 50% correcto
Acá hay dos implementaciones de una función en C para encontrar el punto medio entre un par de valores enteros positivos y ordenados, aproximando hacia abajo. Antes de seguir leyendo, pregúntese cuál es mejor.
int midpoint1(int low, int high)
{
return low/2 + high/2;
}
int midpoint2(int low, int high)
{
return (low + high)/2;
}
midpoint1 es un reloj detenido, retornando 3 en vez de 4 como el punto medio entre 3 y 5, por ejemplo. Entrega la respuesta incorrecta el 25% del tiempo -- fatalmente erróneo si fuera usado en el corazón de, digamos, una búsqueda binaria. Creo que podríamos detectar fácilmente el problema.
Una corrección obvía sería la mostrada en midpoint2, la que retorna 4 como el punto medio entre 3 y 5.
Sin embargo, midpoint2 es un reloj que se retrasa Si la suma low + high produce un desbordamiento (overflow) el resultado es indefinido. En mi implementación obtengo un valor negativo -- una cosa peligrosas si lo usamos como índice de un arreglo. Este es un defecto notorio y muy real, y muy bien documentado en una nota de Joshua Bloch subtitulada ""Casi todas las búsquedas binarias y merge sorts están rotos":http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html".
Bloch ofrece más de una corrección así que voy a hacer notar acá que:
Algoritmos Probabilísticos
Los computadores se suponen que son predecibles y típicamente buscamos programas correctos. No hay razones por las que consideremos programas que sean suficientemente correctos, sin embargo y de hecho muchos programas que son suficientemente buenos para ser útiles también tienen fallas. Los avisos de Google, por ejemplo, analizan el contenido de las páginas webs y despliegan enlaces relacionados. El algoritmo usado es secreto, ingenioso y rápido, pero a menudo resulta en errores semánticos y, en ocasiones, errores ofensivos. Sin embargo, pocos podrían negar cuan útil ha sido para Google este programa.
Acá hay un ejemplo más interesante de un algoritmo, el cual, como un reloj que se atrasa, es casi correcto:
def is_fprime(n):
"""Usa el teorema pequeño de Frmat para adivinar si n es primo.
"""
from random import randrange
tries = 3
xs = (randrange(1, n) for _ in range(tries))
return all((x ** n) % n == x for x in xs)
No entraremos en las matemáticas aquí. Una rápida ejecución con esta función parece prometedora.
>>> all(is_fprime(n) for n in [2, 3, 5, 7, 11, 13, 17, 19]) True >>> any(is_fprime(n) for n in [4, 6, 8, 9, 10, 12, 14, 15]) False
De hecho, si le damos un trabajo real con algunos números grandes, lo hace bien. Lo he usado para adivinar cual de los números entre 100000 and 102000 son primos, comparando la respuesta con el resultado corecto. Tiene una tasa de éxito mejor que un 99% (en términos de relojes, pierde 8 minutos al día) y al aumentar tries mejora su desempeño.
Mejorando is_fprime
Mientras mejor is_fprime se desempeña, menos probable es detectar lo que tiene de malo. Lo que es peor, no puede ser mejorado con ajustes simples. Sin embargo mientras más alto ponemos el valor de la variable tries no tenemos una función correcta. Podríamos aún tomar una prueba al azar de la función y probar todo valor de x en el rango 1 a n dentro del predicado:
def exhaustive_is_fprime(n):
return all((x ** n) % n == x for x in range(1, n))
exhaustive_is_fprime es muy caro para ejecutar y ocasionalmente retornará True para algún número compuesto. Si quieres saber más busca por los números de Carmichael.
El punto que quiero establecer es que el código que es casi correcto puede ser peligroso. Estamos tentados a corregirlo haciendo ajustes a una implementación existente, aunque, en este caso, se requiere de una revisión completa. En contraste, todos sabemos lo que se necesita hacer con el código que es claramente erróneo.
Programación defensiva
Todos hemos visto esas funciones nerviosas que van más allá de su interfaz declarada en un intento de protegerse de los usuarios descuidados.
/** * Retorna el valor máximo encontrado en el arreglo de entrada.
* Pre condición: el arreglo no debe ser vacío.
*/
int nervy_maximum_value(int const * items, size_t count)
{
int M = -INT_MAX;
if (items == NULL || count == 0)
{
return M;
}
for ( ; count-- != 0; ++items)
{
if (*items > M)
{
M = *items;
}
}
return M;
}
Lo que realmente queremos es un más simple y fácil para los clientes que usen este código:
int maximum_value(int const * items, size_t count)
{
int const * const end = items + count;
int M = *items++;
for ( ; items != end; ++items)
{
if (*items > M)
{
M = *items;
}
}
return M;
}
¿Se dieron cuenta del error sutil en nervy_maximum_value? Usa -INT_MAX en vez de INT_MIN lo que causará problemas si los clientes codifican sobre este comportamiento no documentado; si nervy_maximum_value es corregido posteriormente, el código del cliente puede explotar. Noten que no estoy en contra del uso de asertos (assertions) para revisar las pre condiciones, y una revisión simple como assert(items != NULL && count != 0) funciona bien en maximum_value; es la escritura de código que contiene estas condiciones fallidas el que considero erróneo.
Tiempo de vida medio de los defectos.
La ocurrencia de defectos en un sistema de software complejo puede ser modelado de la misma manera que el decaimiento radiactivo. No he estudiado esta teoría y mi física está algo oxidada, pero la idea básica es que la población de errores en un software es como la población de partículas radiactivas.
Un fallo dado aparece (cualquier partícula decae) al azar, así que no podemos predecir cuando este evento sucederá, pero es igualmente probable que surja en cualquier momento particular. Esto le da a cada defecto un tiempo de vida promedio: una expectativa de vida pequeña para los defectos más ruidosos, tales como acceder a un puntero NULL, y tiempos más largos para defectos más sutiles, tales como los errores de redondeo acumulado. Asumiendo que corregimos un bug cuando ocurre, la población de defectos decae exponencialmente, y obtenemos la clásica curva en forma de cola.

Cualquiera que haya tratado de liberar un producto de software sabe lo que se siente deslizarse por la pendiente de esta curva. Probamos el sistema, encontramos defectos, los corregimos, repetimos. Al principio puede ser estimulante en la medida que los "bichos" con corta vida son aplastados, pero hacia el final del juego es desmoralizante ver cómo los defectos son reportados y no pueden ser reproducidos, y nos encontramos sin progresar. Cuando finalmente dibujamos una linea y despachamos el producto lo hacemos sospechando que los peores problemas aún están por ser encontrandos. Para ponerlo en forma sucinta:
Ship happens!
Una combinación de técnicas nos ayuda a escapar de esta imagen depresiva. La más obvia sería evitarla: más que intentar una liberación tipo "big-bang" cada pocos años, podemos movernos hacia una liberación más continua e incremental. Una arquitectura desacoplada ayuda. De ahí la insistencia en pruebas unitarias. En vez de agitar el sistema y ver cómo caen los bichos, deberíamos desarrollar un conjunto de errores automatizados que activamente buscan los varios caminos a través del código y ejercitan los casos de borde. Dentro de la base de código, la programación defensiva puede causar que los defectos se atrincheren. En vez de eso, deberíamos adoptar un estilo más confiado, donde el código falla en forma dura y rápidamente.
¿Cómo llego a funciona ese código alguna vez?
Han arreglado un defecto y luego se han preguntado ¿cómo pudo ese código haber funcionado antes de que lo corrigieran? Es una pregunta importante y una que requiere investigación. Quizás el defecto que arreglaste está compensado por programación defensiva en otro lado. O quizás hay vastas rutas a través del código que aún deben ser probadas.
Conclusiones



Ninguno de estos relojes es muy bueno. El primero está detenido, el segundo pierde un segundo cada minuto, el tercero gana un segundo cada minuto. Al menos es fácil ver el problema con el primero: no nos tentaremos en tratar de parcharlo.
Nunca debermos esperar que nuestro código funcione la primera vez y deberíamos sospechar si así parece. La programación defensiva parece significar cosas distintas a diferentes personas. Si hemos usado mal el térmno aquí, lo siento. Nuestra mejor defensa es asumir que el código está roto hasta que los hayamos probado, asumir que se romperá en el futuro si nuestros test no son automatizados, y fallar rápidamane cuando detectemos errores.
Notas del traductor
(*) Este articulo fue publicado originalmente en julio de 2008, pero ya saben lo que pasó, esta versión tiene algunas correcciones.
[1] Bicho por bug, un defecto en programación.
[2] En Word Aligned hacen ref
erencia a The Rectory Umbrella, pero el texto de Carroll, que yo copio de "Matemática Demente", aparece con el nombre de "Los Dos Relojes".
[3] Texto copiado del libro: "Matemática Demente". Lewis Carroll - Traducción de Leopoldo María Panero, Tusquets Editores, Barcelona 1999
[4] Tal como acota el autor, no es correcto que al tratar de solicitar 4294967295 bytes se provoque un error, malloc retorna NULL en el evento de una falla y el operador estandard new de C++ define como comportamiento disparar la excepción bad_alloc.
Imágenes tomadas de Word Aligned, del artículo original.
"SI estás construyendo aplicaciones web ASP.NET, que van a tener miles de hits por segudo, la sobrecarga de ejecución de las consultas Linq van a consumir demasiada CPU y harán tu sitio web lento. Hay un costo en momento de ejecución asociado con con cada una de las consultas Linq que escribes. Las consultas son analizadas y convertidas a una sentencia SQL en cada hit. Esto no es hecho en tiempo de compilación, porque no hay manera de saber que puedes enviar en los parámetros a las consultas durante la ejecución."
var query = from widget in dc.Widgets
where widget.ID == id && widget.PageID == pageId
select widget;
var widget = query.SingleOrDefault();

start timer
for (j=0;j<batches;j++)
Para el caso de las consultas interpretadas (no pre compiladas):compile query
for (i=0;i<runs;i++)
create new data context
run query in new context
stop timer
print number of selects and time etc.
start timer
for (j=0;j<batches;j++)
for (i=0;i<runs;i++)
create new data context
run query in new context
stop timerprint number of selects and time etc.
Testing 2500 batches of 2 selects
5000 selects uncompiled 9165.0ms 25000 records total 545.55 selects/sec
5000 selects compiled 7892.0ms 25000 records total 633.55 selects/sec
Testing 5000 batches of 1 selects
5000 selects uncompiled 9157.0ms 25000 records total 546.03 selects/sec5000 selects compiled 10825.0ms 25000 records total 461.89 selects/sec
Seguramente se han mareado a estas alturas, pero se los voy a resumir:
Al partir el experimento, se prueba con 1 lote de 5.000 selects. En el caso compilado se logran 925 selects/segundo, y sin compilar 543 selects por segundo. Ganador la versión compilada.
Al final del experimento, se pruena con 5.000 lotes con 1 select. En el caso compilado se logran 461 selects/segundo y sin compilar se logran 546 selects/ segundo.
De hecho si graficamos el tamaño del lote versus el desempeño en selects/segundo, queda claro lo que pasa:

La programación de computadores como un arte
Por Donald Knuth, 1974
(Lee las partes I, II, III y IV)
Menos Facilidades: Más Diversión
Una cosa bastante curiosa que he notado sobre la satisfacción estética es que nuestro placer es significativamente mayor cuando logramos algo con herramientas limitadas. Por ejemplo, el programa del que personalmente estoy muy contento y orgulloso es un compilador de que una vez escribí para una minicomputadora primitiva, que sólo tenía 4096 palabras de memoria, 16 bits por palabra. Una persona se siente como un virtuoso real al lograr algo con tan severas restricciones.
Un fenómeno similar ocurre en muchos otros contextos. Por ejemplo, las personas parecen más a menudo enamorarse de su Volkswagen, pero rara vez con su Continental Lincoln (que probablemente funcionen mucho mejor). Cuando aprendí programación, era un pasatiempo popular hacer todo lo posible con programas que cupieran sólo en una tarjeta perforada. Supongo que es el mismo fenómeno que hace que los entusiastas de APL gocen con sus "one-liners". Cuando enseñamos a la programación hoy en día, es curioso el hecho de que rara vez se captura el corazón de un estudiante de ciencias de la computación hasta que ha tomado un curso que le permite "meter las manos" y ganar experiencia con un miniordenador. El uso de nuestras máquinas a gran escala con sus sistemas operativos y lenguajes de fantasía en realidad no parece generar ningún amor por la programación, por lo menos no al principio.
No está claro cómo aplicar este principio para aumentar el disfrute de los programadores en su trabajo. Seguramente los programadores gruñrán si su administrador de repente les anuncia que la nueva máquina tendrá sólo la mitad de memoria que la antigua. Y yo no creo que nadie, ni siquiera los más dedicados "artistas de programación", vayan a dar la bienvenida a esta perspectiva, ya que a nadie le gusta perder las instalaciones de manera innecesaria. Otro ejemplo puede ayudar a aclarar la situación: Los cineastas se opusieron firmemente a la introducción del cine sonoro en la década de 1920 porque estaban orgullosos de la forma en que podían transmitir palabras sin sonido. Del mismo modo, un artista de programación bien podría resentirse de la introducción de equipos más poderosos, los dispositivos de almacenamiento masivo de hoy en día suelen echar a perder gran parte de la belleza de nuestros viejos métodos de ordenamiento en cintas. Pero los cineastas de hoy no quieren volver a las películas mudas, no porque sean perezosos, sino porque saben que es muy posible hacer películas bellas utilizando tecnología mejorada. La forma de su arte ha cambiado, pero todavía hay mucho espacio para el arte.
¿Cómo desarrollaron estas habilidades? Los mejores realizadores de películas a través de los años por lo general parecen haber aprendido su arte en relativamente primitivas circunstancias, a menudo en países con una industria cinematográfica limitada. Y en años recientes las cosas más importantes que hemos estado aprendiendo sobre programación parecen tener su origen en personas que no tienen acceso a computadores muy grandes. La moraleja de esta historia, me parece, es que debemos hacer uso de la idea de recursos limitados en nuestra propia educación. Todos podemos beneficiarnos de hacer de vez en cuando "programas de juguete", cuando las restricciones artificiales se establecen, de manera que nos vemos obligados a impulsar nuestra capacidad hasta el límite. No debemos vivir en el regazo del lujo todo el tiempo, ya que que tiende a ponernos letárgicos. El arte de la lucha contra los mini problemas con toda nuestra energía va a agudizar nuestro talento para los problemas reales, y los la experiencia nos ayudará a obtener más placer de nuestros logros en equipos menos restringidos.
En una vena similar, no debemos eludir "el arte por el arte", no debemos sentirnos culpables por los programas que son sólo para divertirse. Una vez logre un gran placer alescribir un programa de ALGOL de un sóla sentencia que invocaba un procedimiento de product interno de una manera tan inusual que calculaba el m-ésimo número primo, en lugar de un producto interno. Hace algunos años los estudiantes de Stanford estaban entusiasmados por encontrar el programa más corto en FORTRAN que se imprimiera a sí mismo, en el sentido de que la salida del programa es idéntico al texto de su propia fuente. El mismo problema fue considerado para muchos otros lenguajes. No creo que fuera una pérdida de tiempo para que puedan trabajar en esto, ni que Jeremy Bentham, a quien ya he citado antes, negará la "utilidad" de tales pasatiempos. "Por el contrario," él escribió, "no hay utilidad de que sea más incontestable. ¿En qué se da el carácter de una utilidad, si no es una fuente de placer?
Proveyendo Herramientas Hermosas
Otra característica del arte moderno es su énfasis en la creatividad. Parece que muchos artistas hoy en día no parece importarle tanto la creación de cosas bellas; sólo la novedad de una idea es lo importante. No estoy recomendando que la programación de computadoras deba ser como el arte moderno en este sentido, pero me lleva a una observación que me parece importante. A veces nos asignan una tarea de programación que es casi irremediablemente aburrida, lo que no nos exige en absoluto nada de creatividad, y en esos momentos las personas suelen venir a mí y me dicen: "¿Así que la programación es hermosa? Está muy bien para que usted daclame que uno debería tener el placer de la creación de programas elegantes y encantadores, pero ¿cómo se supone que voy a hacer de este desastre una obra de arte? "
Bueno, es cierto, no todas las tareas de programación van a ser divertidas. Tengan en cuenta a la "ama de casa atrapada", que tiene que limpiar la misma mesa cada día: no hay espacio para la creatividad o el arte en cada situación. Pero incluso en esos casos, hay una manera de hacer una gran mejora: todavía es un placer hacer trabajos de rutina, si tenemos cosas bellas para trabajar. Por ejemplo, una persona puede realmente disfrutar el limpiar la mesa del comedor, día tras día, si es una mesa hermosamente diseñada, a partir de madera de fina calidad.
Por lo tanto, quiero dirigir mis palabras de clausura a los programadores de sistemas y los diseñadores de máquinas que producen los sistemas conque el resto de nosotros debe trabajar. Por favor, déjenos herramientas que sean un placer de usar, especialmente para nuestras tareas de rutina, en lugar de ofrecer algo con lo que tengamos que luchar. Por favor, déjenos herramientas que nos animen a escribir mejor los programas, mediante la mejora de nuestro placer cuando lo hacemos.
Es muy dificil para mí convencer a jóvenes universitarios de que la programación es hermosa, cuando tengo que decirles que es como "slash slash JOB es igual a esto y esto." Aún los lenguajes de control pueden ser diseñados de modo que sea un placer usarlos, en vez de ser estrictamente funcionales.
Los diseñadores de hardware de computador pueden hacer que sus máquinas sean mucho más agradables de utilizar, por ejemplo, proporcionando aritmética de coma flotante que cumpla las leyes matemáticas simples. Las instalaciones disponibles en la actualidad en la mayoría de las máquinas hacen el trabajo de análisis de errores de manera rigurosa e irremediablemente difícil, pero las operaciones de diseño adecuado sería alentar a los analistas numéricos que proporcionaran una mejor subrutinas que hayan certificado la exactitud.
Vamos a considerar también lo que los diseñadores de software pueden hacer. Una de las mejores maneras de mantener arriba el espíritu de un usuario del sistema es proporcionar las rutinas con las que se pueda interactuar. No debemos hacer que los sistemas sean demasiado automáticos, por lo que la acción va siempre detrás de las escenas, debemos dar el programador-usuario la oportunidad de dirigir su creatividad hacia canales útiles. Una cosa que todos los programadores tienen en común es que les gusta trabajar con las máquinas, así que vamos a mantengámoslo en el ciclo. Algunas de las tareas se hacen mejor con la máquina, mientras que otros se hacen mejor con la supervisión humana, y en un sistema bien diseñado se encuentra el equilibrio adecuado. (He estado tratando de evitar la automatización mal dirigida durante muchos años.)
Programar instrumentos de medición es un buen caso de ejemplo. Durante años, los programadores han sido inconscientes de cómo los costos reales de computación se distribuyen en sus programas. La experiencia indica que casi todo el mundo tiene una idea equivocada acerca de los verdaderos cuellos de botella en sus programas, no es de extrañar que los intentos de mejorar la eficiencia vayan mal tan a menudo, cuando a un programador no se le da un desglose de los gastos de acuerdo a las líneas de código que ha escrito. Su trabajo es algo así como la de una pareja de recién casados que intentan planificar un presupuesto equilibrado sin saber cuales serán los costos de los elementos individuales, como alimentos, vivienda y ropa. Todo lo que hemos estado dando a los programadores es un compilador de optimización, que misteriosamente hace algo a los programas que se traduce, pero que nunca explica lo que hace. Afortunadamente ahora estamos finalmente viendo la aparición de sistemas que le dan el crédito de usuario para una cierta inteligencia, sino que proporciona automáticamente la instrumentación de programas y la información apropiada sobre los costos reales. Estos sistemas experimentales han sido un gran éxito, ya que producen mejoras mensurables, y sobre todo porque es divertido de usar, así que confío en que es sólo cuestión de tiempo antes de que el uso de estos sistemas sea un procedimiento operativo estándar. Mi artículo en Computer Survey discute esto aún más, y presenta algunas ideas para otras formas en que una rutina interactiva apropiada puede mejorar la satisfacción de los programadores usuarios.
Los diseñadores del lenguaje también tienen la obligación de proporcionar los lenguajes que fomenten un buen estilo, ya que todos sabemos que el estilo es fuertemente influenciado por el lenguaje en el que se expresa. La oleada actual de interés en la programación estructurada ha puesto de manifiesto que ninguno de nuestros lenguajes existentes es realmente idóneo para abordar el programa y la estructura de datos, ni tampoco está claro como debe ser un lenguaje ideal. Por lo tanto, espero que muchos hagan experimentos cuidadosos en el diseño el lenguaje durante los próximos años.
Resumen
En resumen, hemos visto que la programación de computadoras es un arte, porque se aplica el conocimiento acumulado en el mundo, porque requiere habilidad e ingenio, y especialmente, debido a que produce objetos hermosos. Un programador que inconscientemente, se ve a sí mismo como un artista disfrutará de lo que hace y lo hará mejor. Por lo tanto, podemos alegrarnos de que la gente que da charla en las conferencias de computación hable sobre estado del arte.
La programación de computadores como un arte
Por Donald Knuth, 1974
Es mi propósito transmitir la importancia del buen gusto y estilo en la programación, [pero] los elementos específicos de estilo presentados sirven sólo para ilustrar los beneficios que pueden ser derivados del "estilo" en general. Al respecto me siento afin al profesor de composición en el conservatorio: el no les enseña a sus pupilos como componer una sinfonía particular, el debe ayudar a sus pupilos a encontrar su propio estilo y debe explicarles las implicancias de esto. (Es esta analogía la que me hace hablar del "Arte de la Programación".)Ahora debemos preguntarnos, ¿qué es buen estilo, y qué es mal estilo? No deberíamos ser demasiados rígidos sobre esto cuando juzguemos el trabajo de otras personas. El filósofo de principios del siglo diecinueve Jeremy Bentham lo puso de esta manera:
Los jueces de la elegancia y del gusto se consideran a si mismos como benefactores de la raza humana, cuando en realidad sólo son los interruptores del placer... No existe el gusto que merezca el epíteto de bueno, a menos que sea un gusto empleado de tal forma, que el placer actualmente producido por este, al unirlo con algo contingente o futuro sea de utilidad; no hay gusto que merezca ser llamado malo, a menos que sea un gusto ocupado para algo ue tenga una tendencia perjudicialCuando aplicamos nuestros prejuicios para "reformar" el gusto de alguien más, estamos inconcientemente negándole algo de su enteramente legítimo placer. Es por esto que no condeno un montón de las cosas que hacen los programadores, aunque nunca disfrutaría haciéndolas yo mismo. Lo importante es que ellos están creando algo que ellos sienten que es hermoso.
La programación de computadores como un arte
Por Donald Knuth, 1974
Ciencia y Arte
Nuestra discusión indica que la programación de computadores es para ahora ambas: una ciencia y un arte, y que los dos aspectos se complementan bien una con otra. Aparentemente muchos autores que examinan tal pregunta llegan a la misma conclusión, que su área de estudio es ambas una ciencia y un arte, cualquiera sea esta área. Encontré un libro sobre fotografía elemental, escrito en 1893, que decía "el desarrollo de la imagen fotográfica es ambas un arte y una ciencia." De hecho, cuando tomé primero un diccionario para estudiar las palabras "arte" y "ciencia" le eché un vistazo al prefacio del editor, el que empezaba diciendo, "la construcción de un diccionario es una ciencia y un arte a la vez". El editor del diccionario de Funk & Wagnall's dictionary observaba que la penosa acumulación y clasificación de los datos sobre las palabras tiene un carácter científico, mientras que un fraseo bien escogido para la definición demanda la habilidad de escribir con la economía y la precisión: "La ciencia sin este arte es probablemente inefectiva, el arte sin la ciencia es ciertamante inexacto."
Cuando preparaba esta charla miré las tarjetas del catálogo en la biblioteca de Standford para ver como otras personas han usado las palabras "arte" y "ciencia" en los títulos de sus libros. Esto resultó bastante interesante.
Por ejemplo, encontré dos libros titulados "El arte de Tocar el Piano" y otros llamados La Ciencia de la Técnica del Pianoforte, La Ciencia de la Práctica del Pianoforte. Hay también un libro llamado El Arte de tocar Piano: Una Aproximación Científica.
Entonces encontré un simpático librillo titulado El Gentil Arte de las Matemáticas, el que me puso algo tristre ya que honestamente no puedo describir la programación de computadores como un "arte gentil". He sabido por muchos años de un libro llamado El Arte de la Computación, publicado en San Francisco en 1879, por un hombre llamado C. Frusher Howard. Era un libro sobre aritmética práctica para los negocios que ha vendido sobre 400.00 copias en varias ediciones hacia 1890. Me asombré al leer el prefacio, pues muestra que la filosofía de Howard y la intención del título era bastante diferente de la mía, el escribe: "El conocimiento de la ciencia de los números es de la menor importancia, el arte de ajustar las cuentas es absolutamente indispensable."
Varios libros mencionan a ambas la ciencia y el arte en sus títulos, notablemente La Ciencia del Ser y el Arte de Vivir por el Maharishi Mahesh Yogi. Hay también un libro llamado El Arte del Descubrimiento Científico, el que analiza como algunos de los grandes descubrimientos de la ciencia se han hecho.
Suficiente de la palabra "arte" en su sentido clásico. Realmente cuando escogí el título de mis libros, yo no estaba pensando principalmente en arte en este sentido, estaba pensando más en sus connotaciones actuales. Probablemente el libro más interesante que inició mi búsqueda fue un trabajo reciente de Robert E. Mueller llamado La Ciencia del Arte. De todos los libros que he mencionado, el de Mueller es el que más se acerca a expresar lo que quiero hacer el tema central de mi charla hoy, en términos artísticos reales tales como entendemos ahora el término. El observa: "Una vez se pensó que las perspectivas imaginativas del artista eran la muetre del científico. Y la lógica de la ciencia parecía significar la perdición a todos los posibles vuelos de la imaginación artística." El continúa explorando las ventajas que resultan de la síntesis de la ciencia y el arte.
Una aproximación científica es caracterizada generalmente por las palabras lógica, sistemática, impersonal, calma, racional, mientras que la aproximación artística se caracteriza por la palabras estética, creativa, humanitaria, ansiosa, irracional. Me parece que esta aparente contradicción de aproximaciones tiene gran valor con respecto a la programación de los computadores.
Emma Lehmer escribió en 1956 que había encontrado que la programación era "una ciencia exacta junto con un arte intrigante." H.S.M. Coxeter recordaba en 1957 que a veces se sentía "más como un artista que como un científico". Estos eran los tiempos en que .P. Snow empezó a vocear su alarma sobre la creciente polarización de "las dos culturas" entre la gente educada. El apuntó que necesitamos combinar los valores científicos y artistico y queremos hacer progresos reales.