Pensando en programar

Nota: Utilizo la palabra problema en el sentido general de la siguiente acepción incluida en el DRAE:

Planteamiento de una situación cuya respuesta desconocida debe obtenerse a través de métodos científicos.

Así un problema para nosotros será en general cualquier situación en la que nos planteemos un cierto objetivo a alcanzar por medio de la aplicación de -probablemente- algún sistema de software, que es lo que tendremos que desarrollar o programar.

Llegan los problemas

A la hora de resolver un problema, los primeros pasos son, la mayoría de las veces, los más importantes. El primero de todos ellos debe ser el análisis y comprensión del propio problema.

En tiempos recientes, cualquier idea que suene o parezca sonar a “análisis” o a “fases” o “pasos” parece que encuentre una cierta oposición. Esto es debido sobre todo a quienes aplican y entienden mal las ideas de las metodologías llamadas Agile 1). Como ya he dicho en alguna ocasión no tengo intención de tratar estos temas, pero es conveniente señalar que ese tipo de metodologías no son realmente metodologías de programación, sino que se centran en la gestión del desarrollo y en dinámicas de gestión de equipos. Nosotros estamos hablando específicamente de programar, en una perspectiva que asumimos es fundamentalmente personal. Esto no quiere decir que sea uni-personal, pero sí que es importante resaltar que nos estamos moviendo a un nivel completamente diferente.

La comprensión del problema seguramente sea la fase más importante de todo el proceso y la que más retorno proporciona sobre el tiempo que invirtamos en ella. En el fondo, si no hemos llegado a comprender correctamente un problema, ¿cómo podemos siquiera pretender ser capaces de resolverlo? Por tanto, es una tarea crítica y deberemos dedicarle todo el esfuerzo que sea razonable dedicarle. Más adelante, cuando hablemos de “economía del código” veremos con más claridad cuánto es razonable o no dedicar a una tarea. Por ahora quedémonos con la idea de que el análisis y comprensión del problema, no solo es el primer paso sino probablemente el más importante.

Análisis

Existen muchas técnicas más o menos diferentes o similares para el análisis, y todas ellas tienen abundantes explicaciones y guías disponibles. Yo voy a contar cómo me enfrento yo al análisis de los problemas desde el punto de vista de la programación. No creo que sea una forma intrínsecamente mejor que otras, pero a mi me funciona y no es algo esencialmente diferente o innovador respecto a lo que he visto que hacen otras personas.

Lo que sigue no debe entenderse como un orden particular. Son varias tareas u objetivos a alcanzar, pero generalmente se llevan a cabo de manera más o menos simultánea.

Identificación de las partes

En todo problema podemos encontrar diferentes partes o elementos que intervienen. No se trata solo de identificarlos como tales, sino de, a la vez que lo hacemos, identificar su relevancia y su papel en el problema y relación con otras partes.

Tomemos por ejemplo el caso de que nos planteemos desarrollar un juego como el Parchís. Aquí podríamos identificar muchas partes diferentes. Inicialmente podemos ver algunas con cierta claridad: Jugadores, tablero, fichas, casillas, dados… Algunas no serán tan visibles o evidentes, como por ejemplo el concepto del turno. El turno no es algo palpable, pero parece por lo menos en primera instancia considerar que de algún modo gestionar y seguir el turno es relevante dentro del juego.

Podemos caer, sin querer, en una situación en la que aparentemente empecemos a tener decenas, cientos, miles de partes. El problema se vuelve más complejo cuantas más partes diferentes consideremos. Por eso mismo, es importante considerar las siguientes dos tareas del análisis.

Es interesante también no penar solo en “partes” como elementos directos del problema. Como nombres. Quiero decir con esto que estas “partes del problema” no solo con cosas que intervienen, también pueden ser cálculos, procesos, acciones, etc, que descubrimos que habrá que hacer. El caso del turno que mencionaba antes. Podemos pensar en el turno como un elemento, pero en realidad es más una mecánica, un proceso o una gestión que interviene en el problema.

En el próximo capítulo, donde veremos cómo analizar algunos casos prácticos como ejercicio, intentaré cubrir también esta idea.

Definir niveles de detalle

Casi cualquier problema que nos encontremos, se podrá contemplar desde diferentes perspectivas o en diferentes niveles de abstracción. A mi, generalmente, me gusta llamar a esto niveles de detalle. En parte porque creo que es más fácil de entender para quien no esté acostumbrado y en parte también porque creo que recuerda constantemente dónde está la clave para definir un determinado nivel: el tamaño del detalle con el que lo consideramos.

Me recuerda también, una técnica que hay en videojuegos y en pintura, y en general en casi todos los sistemas de representación un poco elaborados. Seguramente os resultará familiar. Se trata simplemente de representar con mayor detalle las cosas que están cerca y con menos las que están lejos. Me recuerda la historia de Goya y los techos de la Basílica del Pilar de Zaragoza. Se cuenta que Goya pintaba muy rápido a base de aprovecharse de que la altura de techos hacía que las pinturas siempre se verían desde lejos y por tanto, no necesitaba dedicar tanto esfuerzo a los detalles más pequeños puesto que no se iban a ver.

Necesitaremos definir siempre un primer nivel de detalle de visión de conjunto. Aquí definiremos muy poco detalle y solo consideraremos un pequeño número de partes claras y relevantes. No tenemos que preocuparnos por analizar cada una de estas partes, sino identificar 3, 4, quizá media docena, de elementos fundamentales y su papel general dentro del problema. Siguiendo el ejemplo del Parchís podríamos considerar, por ejemplo, jugadores (así en conjunto), tablero, fichas, turno de juego. No consideramos, por ejemplo, las casillas individuales. Puede que estas terminen siendo una entidad concreta en nuestro problema, pero a este nivel consideramos que las casillas son parte del tablero. No consideramos los dados tampoco, porque es más bien un mecanismo concreto para decidir el movimiento. Podríamos decir que son un detalle de implementación.

Además de esto, es habitual, aunque dependerá de cada caso, definir algunos niveles de detalle más. En muchos casos, estos niveles no solo son más detallados, sino que solo se centran en una parte concreta del problema. De ahí que consideremos la siguiente de las tareas del análisis.

Descomposición

Existen problemas de muy diferentes alcances, como es obvio. Algunos son tan triviales que se alcanzan a comprender con una única visión de conjunto y apenas intervienen en ellos una o dos partes. Pero esto, en general, no será lo común.

La mayoría de las veces nos encontraremos problemas de una complejidad alta, con muchos detalles y un número relativamente grande de partes diferentes y muchas relaciones entre ellas. La herramienta más potente de que disponemos para analizar este tipo de situaciones, es utilizar la descomposición.

Se trata simplemente de separar diferentes elementos, limitando su alcance a una parte o aspecto más pequeño del problema entero, de modo que nos permita analizar cada uno de la forma más independiente posible. Hacer esto bien es casi un arte, en el sentido de que requiere sobretodo experiencia y el desarrollo de una cierta intuición o habilidad para identificar esas partes. Personalmente la descomposición se ve reflejada en uno de los dos principios fundamentales que yo me planteo en la programación:

Junta lo que va junto. Separa lo que va separado.

Esto, que también aplico al propio código, es una expresión directa de las tareas de descomposición de problemas. Es cierto que no siempre es fácil delimitar qué va junto o separado. Una forma más o menos eficaz de establecer las separaciones consiste en observar las relaciones que hay entre las partes. Partes separadas pueden, como es natural, tener relaciones entre ellas, pero generalmente estas suelen ser pocas y claras, bien definidas. Si observamos que dos cosas que considerábamos independientes, tienen múltiples puntos de relación, dependencias que van en ambos sentidos (la parte A depende de B y la parte B depende de A), es bastante posible que no vayan tan separadas como creíamos.

En ocasiones lo que observaremos es que todas las relaciones de una de las partes son exclusivamente con otra de las partes. Si vemos algo así, es bastante probable que en realidad sean dos partes de una misma cosa o, visto de otro modo, que hayamos intentado descomponer una unidad a un nivel de detalle demasiado bajo. La descomposición debe hacerse siempre teniendo en cuenta el nivel de detalle que estemos considerando en cada caso. En el ejemplo del Parchís, recordemos que inicialmente no habíamos considerado las diferentes casillas como un elemento relevante. Sin embargo, una vez descompuesto el problema general, cuando examinemos un nivel de detalle enfocado exclusivamente en el tablero, entonces será razonable considerar que a ese nivel las casillas sí son relevantes y tienen entidad propia.

Problema vs Solución

El punto fundamental, la clave de esta fase, y lo que debemos mantener siempre en nuestra mente cuando realizamos la tarea de análisis es que problema y solución son dos cosas completamente diferentes.

Al hacer un análisis, tenemos que centrarnos por completo en observar y comprender. Este es el objetivo único de la tarea. Queremos establecer qué es lo que queremos resolver. En ningún momento tenemos que dejarnos llevar a dar el salto y ponernos a plantearnos cómo lo vamos a resolver.

Por esto, cuando hablamos, por ejemplo, de identificar partes o relaciones o de establecer niveles de detalle, nunca he mencionado nada específico sobre qué, por ejemplo, cómo vamos a implementar una cierta relación o qué va a implicar manejar un cierto elemento. Eso son temas que pertenecen a la solución, no al problema. Al cómo, no al qué.

Insisto en recalcar la importancia de esto.

Si nos dejamos llevar y mezclamos el qué con el cómo, es muy fácil que caigamos en uno de estos errores, o en ambos:

  • Es fácil que ignoremos información del problema. Si nos acercamos con una idea preconcebida de cómo vamos a solucionarlo, terminaremos por, consciente o inconscientemente, tratar de adaptar el problema a nuestra solución en lugar de lo contrario, que es lo que debemos hacer.
  • Es también fácil que terminemos asumiendo algunas partes de nuestra solución como parte del problema original y esto nos dificulte contemplar otras soluciones que pueden ser tan válidas o incluso más. Además, como es evidente, nadie quiere que la solución pase a ser parte del problema ;)

Diagramas, esquemas, notas

Cuando yo hago este análisis, suelo terminar tomando algunas notas y a veces haciendo algún tipo de diagrama o esquema. Estos son solo una herramienta más en el análisis. No debemos entender que nada de ello es el producto del análisis, ni que estos apuntes son algún tipo de documentación que podamos compartir.

El producto del análisis del problema es nuestro conocimiento, es la comprensión del problema. Las notas o diagramas que hagamos son únicamente para ayudarnos a nosotros. Esto es porque son notas que tomamos mientras aún estamos haciendo el análisis. Así, no tenemos una comprensión completa todavía, y cualquier nota o diagrama contendrá, muy probablemente, pensamientos parciales, correcciones, anotaciones temporales, o muchas veces símbolos que sirven como indicación para el conocimiento que está, en realidad, en nuestra cabeza.

Si lo que queremos es crear algún tipo de documento (sea escrito o en forma de diagrama o esquema) que sirva para comunicar o explicar el problema a otros, entonces lo tendremos que crear de forma explícita y esto será después de haber adquirido nosotros una comprensión suficiente del problema, es decir, después de haber realizado el análisis, no mientras aún lo estamos haciendo.

Por esto mismo, recomiendo que no nos preocupemos demasiado de estas notas, más allá de lo que nos sirva a nosotros mismos. Por ejemplo, no es necesario utilizar ningún tipo de formato o de notación específicos o formales.

1)
¿O quizá, lamentablemente, de quienes las entienden y aplican bien?

Discusión

Escribe el comentario. Se permite la sintaxis wiki: