miércoles, 27 de octubre de 2010

¡Escoge bien tu arma!

¡Bienvenidos programadores! A mi me sucedió, y posiblemente a muchos otros también, que cuando comencé a interesarme en los maratones de programación lo único que quería era continuar resolviendo más y más problemas. En busca de seguir mejorando, investigamos nuevos y mejores algoritmos, nuevas y mejores estructuras de datos. Sin embargo, a veces estamos seguros de la solución de un problema, pero no tenemos idea de como implementarla en nuestro lenguaje de programación favorito. Incluso, a veces lo logramos pero nos damos cuenta de que tardamos demasiado en terminarlo y realmente no aprendimos demasiado al hacerlo. Puede incluso ocurrir también que el programa que resultó no sea lo suficientemente rápido para ejecutarse bajo el tiempo límite del problema.

Sucede que el primer paso y uno de los más importantes a la hora de empezar a programar soluciones a estos problemas, es el de escoger el lenguaje con el cual se piensa implementarlo. Y no solo para los maratones de programación, sino para cualquier aplicación que se desee realizar. Lo primero que se debe hacer es planificar que lenguajes de programación y herramientas conviene utilizar. Esto, ya sea para que sea más fácil de implementar, se ejecute velozmente, sea portable, mantenible o cualquier otra característica que se considere importante.

Muchas personas que tienen un tiempo compitiendo en los maratones de programación recomendarían que uno escogiera el lenguaje con el que uno se siente más cómodo (algunos incluso parecerán decirte, que si no escoges C++ no estás en nada, jajaja). Aprender todos los detalles de ese lenguaje, sus librerías, la manera rápida de manejar la entrada/salida, etc. Yo sin embargo, creo que es más importante estudiar TODOS los lenguajes disponibles y analizar cuales son sus fortalezas y debilidades. Así, podré decidir cual lenguaje me conviene más utilizar dado un problema específico.

Ahora, no digo que uno no tenga un lenguaje insignia, aquel que nos se más cómodo. Es importante conocer al detalle este lenguaje y hacer especial énfasis en estudiarlo. Sin embargo, se debería dedicar también un poco de estudios a los demás lenguajes de programación disponibles, pudiendo decidir entonces cuando nuestro lenguaje insignia es idóneo y cuando es vulnerable. Los maratones de programación por lo general son en equipos de 2 a 3 personas, por lo que una buena estrategia es intentar lograr que los lenguajes de programación insignia de cada integrante sean distintos. Así, cada uno conoce un poco más de su propio lenguaje, sin embargo con la visión de poder decidir que un problema es más adecuado para que lo resuelva su compañero. Este tipo de estrategias lleva mucho entrenamiento, quizá incluso más que la resolución misma de los problemas por uno mismo.

Hay muchos lenguajes de programación disponibles, diseñados bajo distintos conceptos e ideales en diferentes áreas. Sin embargo, hablaré un poco sobre los lenguajes de programación ofrecidos en los maratones de programación ACM-ICPC. Estos son: C/C++ y Java. Siendo C un subconjunto de C++, es claro que la elección real es entre C++ y Java. Si bien ambos lenguajes tienen muchas similitudes, diseñados bajo el mismo esquema de orientación a objetos, la verdad es que tienen diferencias notables que pueden ser aprovechadas a la hora de programar la solución a un problema.



Comienzo por mi propio lenguaje insignia para los maratones: Java. Este lenguaje es poderoso y a la vez elegante. Es verdad que pide que se escriba más, pero a cambio es consistente en la mayoría de sus definiciones. ¿Su gran desventaja? Es muy lento y consume mucha memoria. Por lo tanto, para problemas que requieren mucha manipulación de datos y un bajo nivel de abstracción de los mismos (Como los problemas de programación dinámica, de los cuales se hablará un post futuro), Java no es la mejor elección. La memoria y el espacio de pila se ven seriamente afectados por el hecho de ejecutarse bajo una máquina virtual. ¿Y entonces para que problemas es ideal? Principalmente para aquellos en los que la abstracción de los datos sea vital (Como algunos problemas de grafos) o los problemas geométricos.

Por ejemplo, en el paquete "java.awt" pueden encontrarse muchísimas clases útiles que resuelven problemas geométricos típicos de los maratones. Por ejemplo, en la clase "java.awt.Polygon" hay funciones para determinar si un punto dado está dentro de un polígono o incluso un rectángulo completo, puede calcular el "bounding box" para el polígono y hasta saber si el polígono intersecta con un rectángulo dado. Si bien es posible y quizá no tan difícil implementar estas funciones, es en verdad muy útil no tener que hacerlo durante una competencia. Siendo parte del API se puede asegurar que estas funciones están correctas e implementadas eficientemente.



Ahora, el otro lenguaje disponible para resolver los problemas: C++. Es el lenguaje insignia para la mayoría de los programadores al resolver problemas. ¿Cual es la razón? Es extremadamente eficiente y rápido para codificar. A cambio de perder cierta consistencia en sus definiciones, C++ permite programar de forma muy eficiente, incluso aplicaciones que usen abstracción de datos. En la mayoría de los casos, se utiliza el Standard Template Library (STL). Al ser compilado directamente a la máquina puede hacer un mejor manejo de la memoria y los demás recursos disponibles. ¿Para que tipo de problemas no sirve entonces? Por ejemplo, para aquellos en los que se deba hacer manipulación fuerte de cadenas de caracteres (cosa que se puede mejorar un poco usando stringstreams) o problemas donde hace falta precisión arbitraria de números. A diferencia de Java, que provee las clases "java.math.BigInteger" y "java.math.BigDecimal", en C++ se deben implementar estas funcionalidades manualmente cada vez que desee hacerse este tipo de cálculos. ¿Y para que problemas si es ideal? Principalemente para aquellos en donde contar con la mayor cantidad de memoria posible y una rápida ejecución es vital, como la programación dinámica. Así también para los programas cortos que no necesitan de muchas estructuras de datos, pues es mucho más rápido de codificar.

En conclusión C++ y Java, lenguajes muy similares en su origen, se ven distinguidos por decisiones de diseño que tomaron cada uno de sus implementadores. C++ es un lenguaje diseñado para ser expresivo, eficiente y compacto en su escritura. Mientras que Java es diseñado con el objetivo de ser consistente, fácil de entender y con una gran cantidad de librarías para ahorrar trabajo al programador. Cada quien debe encontrar cual de estos será su lenguaje insignia, recordando que hay que conocer también al otro, pues puede salvar un problema en medio de una competencia. Mi consejo es que intenten resolver sus primeros problemas en ambos lenguajes y decidir con cual se sienten más cómodos, nunca dejando que otras personas con aparentemente más experiencia les obliguen a entrenar en el lenguaje que ellos prefieran, pues entonces el entrenamiento será en vano.

Ambos lenguajes tienen una gran cantidad de editores inteligentes que pueden ser utilizados para implementar el código con mayor rapidez. Hacerse familiar con estas herramientas también es una parte importante del entrenamiento, pues ahorrará tiempo a la hora de codificar los problemas. Sin embargo, es importante no hacerse dependiente de dichos editores, pues algunas competencias no los ofrecen y se pierden algunas de las ventajas de correr los programas directamente sobre la máquina (en especial para C++). El IDE (Inteligent Development Environment) posiblemente más utilizado, ideado originalmente para Java pero que ahora comprende una amplia variedad de lenguajes, es Eclipse.

Por último, espero que este post les haya gustado y haya servido para empezar a decidir que lenguajes utilizar al atacar un problema. Cualquier comentario o sugerencia es más que bienvenido. ¡Muchísimas gracias por leer!


4 comentarios:

  1. Muy interesante. Soy de esos que normalmente usa java. PD. Me entero que la librería java.awt podía hacer esas cosas. Sigue así Bro.

    ResponderEliminar
  2. Excelente post!

    Al igual que quién comentó antes que yo, no tenía idea que la librería java.awt pudiera hacer lo que dices, ya que de ser así simplifica varios problemas geométricos.

    Antes de leer tu post ya pensaba en comenzar a aprender Java (uso C++/STL) para las competencias, principalmente por el uso de Bignum. Ahora estoy convencido :)

    Espero de frutos este blog, ya que sería de mucha ayuda para la gran cantidad de personas que estamos interesados en competencias de programación y temas afines.

    Saludos!

    ResponderEliminar
  3. Eso me recuerda a ciertas conversaciones que tuve con un pana de la universidad que me da clases de algoritmos y matemáticas. xD

    Ahora bien, lo que sí me gustaría que comentaras en un futuro post, y si estás de acuerdo, es que la herramienta debe ser aprendida antes de intentar resolver el problema y no a la par. Por experiencia propia, se hace cuesta arriba aunque el problema sea trivial.

    ¡Bytes!.

    ResponderEliminar
  4. ¡Muchas gracias por sus comentarios!

    Intentaré publicar periódicamente sobre librerías interesantes que pueden utilizarse para resolver cierto tipo de problemas, jeje. En particular, "java.awt" es super poderoso, quizá le dedique un post completo en otra ocasión. :D

    @pctroll: Como dices, es importante tener conocimientos en el lenguaje que se quiera implementar antes de comenzar a solucionar todos los problemas. Sin embargo, tampoco es bueno quedarse estancado aprendiendo y no resolver problemas también, ya que se puede comenzar a perder el entusiasmo. Hay pocas cosas más motivantes, en mi opinión, que un "Accepted" en un problema que hemos estado trabajando. Sin eso, ponerse solamente a investigar sobre las librerías puede llegar a ser tedioso y finalmente hacer que se pierda el impulso de seguir practicando. :P

    ResponderEliminar