El fin de semana pasado publiqué una versión nueva de ZinjaI con muchas mejoras, aunque la mayoría no son fácilmente visibles. Hay pequeños detalles por aquí y por allá en la interfaz, hay mejoras en casos particulares para el autocompletado, hay algún que otro cambio en la configuración de proyecto para simplificar algunos nuevos complementos, etc. Aunque también hay cosas que se ven, como la nueva ventana de ayuda sobre C++ que permite explorar offline el contenido ofrecido por cppreference (con licencia CC, instalable como complemento); o el nuevo cuadro de configuración para la integración de proyectos con wxFormBuilder (de lo que hablaré en breve en otro post). Pero el tópico de hoy apunta a comentar qué grandes cambios (visibles o no) estoy pensando a futuro. Algunos para la próxima versión, otros tal vez tarden un poco más en llegar.
viernes, 28 de marzo de 2014
jueves, 13 de marzo de 2014
El autocompletado y yo (capítulo 2)
Ya comenté hace mucho cómo funcionaba el sistema de autocompletado en ZinjaI, y cuales eran sus limitaciones. Básicamente, por un lado tengo a cbrowser, una herramienta que analiza los fuentes y me dice qué cosas se definen. Su parseo es mucho más rápido que el de un compilador, ya que no usa toda la información disponible, pero aún así no es tan rápido como para aplicar en tiempo real. Por eso solo se aplica cada vez que se guarda un archivo fuente, completando con su información el árbol de símbolos. Luego, como el autocompletado es necesario mientras estamos editando un fuente, tiene que haber otro mecanismo en tiempo real para compensar la falta de actualización de esa información. Y además, mientras lo editamos, el código no es válido, está a mitad escribir, y en ese caso el análisis de cbrowser tampoco funcionaría. Entonces tengo un mecanismo alternativo que analiza un fuente abierto, mirando solo ese fuente (y solo cerca de donde estemos editando), y aplicando una heurística propia y poco fiable, pero muy rápida y tolerante a errores de sintaxis. Esencialmente se encarga de dos cosas: tratar de averiguar de qué tipo es una variable mirando si hay algo que pueda ser su definición (sino se buscará en el árbol de símbolos), y averiguar qué representa hasta el momento una expresión a mitad escribir.
Hace un tiempo que me decidí a reescribir este último mecanismo. Lo quise reescribir porque estaba fragmentado en diferentes lugares de ZinjaI y entonces había algunas funcionalidades repetidas, y otras difíciles de reutilizar. Mi idea era mejorar el diseño para agregar algunas mejoras muy útiles, y de paso tratar de que todo se analice con el mismo código para simplificar el mantenimiento y acotar los problemas. En este artículo entonces voy a describir los problemas a los que me enfrento al hacer esto, y qué hay de nuevo para la próxima versión de ZinjaI.
Hace un tiempo que me decidí a reescribir este último mecanismo. Lo quise reescribir porque estaba fragmentado en diferentes lugares de ZinjaI y entonces había algunas funcionalidades repetidas, y otras difíciles de reutilizar. Mi idea era mejorar el diseño para agregar algunas mejoras muy útiles, y de paso tratar de que todo se analice con el mismo código para simplificar el mantenimiento y acotar los problemas. En este artículo entonces voy a describir los problemas a los que me enfrento al hacer esto, y qué hay de nuevo para la próxima versión de ZinjaI.
lunes, 10 de marzo de 2014
El programador es un pequeño Dios
Empieza una vez más el cursado en "mi" Universidad, y como docente de una de las comisiones de teoría de la materia "Fundamentos de Programación" tengo la enorme responsabilidad de presentarles la programación por primera vez a muchos jóvenes ingresantes. Es una oportunidad única para que empiecen motivados y con el pie derecho, para que lo vean como algo atrapante y divertido, y no como una materia más. Y es en este contexto, donde cada año al preparar esos primeros 15 minutos de la primer clase, divago sobre la naturaleza de la programación, como lo voy a hacer en este post. Pero antes de seguir leyendo, estén advertidos de que será una ensalada de cosas muy subjetivas relacionadas de formas cuestionables.
Miles de artículos se han escrito debatiendo acerca de la naturaleza de la programación, ¿arte o ciencia? Si me preguntan a mi, es ambas cosas, pero primero digo arte, porque lo de ciencia lo doy por obvio, mientras que lo de arte es lo más discutido. De todas, formas, no es esta pregunta el eje de este post, sino que pretendo volcar en algunas lineas una visión general y motivadora de la parte que más disfruto de esta maravillosa actividad, la visión que me gustaría transmitir a mis alumnos y compañeros para alentarlos a disfrutar de los desafíos como yo lo hice cuando estaba aprendiendo, y como lo sigo haciendo en la mayoría de los casos.
Miles de artículos se han escrito debatiendo acerca de la naturaleza de la programación, ¿arte o ciencia? Si me preguntan a mi, es ambas cosas, pero primero digo arte, porque lo de ciencia lo doy por obvio, mientras que lo de arte es lo más discutido. De todas, formas, no es esta pregunta el eje de este post, sino que pretendo volcar en algunas lineas una visión general y motivadora de la parte que más disfruto de esta maravillosa actividad, la visión que me gustaría transmitir a mis alumnos y compañeros para alentarlos a disfrutar de los desafíos como yo lo hice cuando estaba aprendiendo, y como lo sigo haciendo en la mayoría de los casos.
viernes, 28 de febrero de 2014
Excusas para no documentar un código
En un proyecto de software tenemos dos tipos básicos de documentación. Por un lado está la documentación para el usuario final, para que aprenda a utilizarlo, como los manuales de ayuda o las referencias, mientras que por otro lado está la documentación sobre el desarrollo del mismo, como los diagramas de clases, casos de uso, o los comentarios en el código por ejemplo. Quiero hablar particularmente de los últimos, de los comentarios en el código (lo que se conoce como documentación interna) porque es algo que creo que a la mayoría no nos simpatiza mucho tener que hacer, pero que tampoco podemos delegar. El manual de usuario, por ejemplo, sí lo podría hacer por otra persona que no sea la que codificó el programa. Pero la documentación interna en general es responsabilidad directa del programador.
Como ya dije, es algo que consume tiempo y ganas, y por eso solemos inventar mil excusas para no hacerlo (o si es un proyecto individual, directamente no lo hacemos, sin tener que rendirle excusas a nadie). Pero a la larga, las consecuencias de una mala documentación terminan costando todavía más tiempo y paciencia (mucho más) de lo que habría costado hacerlo bien en su momento. Yo lo aprendí por las malas, mis códigos viejos son claras evidencias de ello, y por eso ahora vengo a refutar las excusas más comunes que nos ponemos para saltearnos ese trabajo.
Como ya dije, es algo que consume tiempo y ganas, y por eso solemos inventar mil excusas para no hacerlo (o si es un proyecto individual, directamente no lo hacemos, sin tener que rendirle excusas a nadie). Pero a la larga, las consecuencias de una mala documentación terminan costando todavía más tiempo y paciencia (mucho más) de lo que habría costado hacerlo bien en su momento. Yo lo aprendí por las malas, mis códigos viejos son claras evidencias de ello, y por eso ahora vengo a refutar las excusas más comunes que nos ponemos para saltearnos ese trabajo.
jueves, 13 de febrero de 2014
Polimorfismo en psexport
Psexport es uno de los módulos de PSeInt, el que se encarga de traducir un algoritmo en pseudocódigo a lenguajes de programación reales como C, C++, Java, PHP, etc. En realidad hace la mitad del trabajo. Traducir implica primero entender qué es lo que está escrito en un lenguaje, para luego ver cómo escribirlo en el otro. La primer parte la hace el mismísimo intérprete, generando un archivo intermedio "premasticado" para que psexport lea más fácilmente y dedique sus mayores esfuerzos a ver cómo sería mejor escribirlo en ese otro lenguaje. Inicialemente, ese otro lenguaje era solo C++, pero recientemente se añadieron unos cuantos más. La orientación a objetos y particularmente el polimorfismo hicieron que pueda programar todas esas nuevas traducciones con muy poco esfuerzo. Así que en este post les voy a mostrar un ejemplo del tan valioso y no siempre bien ponderado polimorfismo.
viernes, 7 de febrero de 2014
Polimorfismo en C++
El uso del polimorfismo en C++ es uno de los temas que más complicaciones les trae a los estudiantes cuando están haciendo sus primeras armas en C++ y Programación Orientada a Objetos. Aclaro que estoy hablando de clases abstractas y métodos virtuales, porque algunos materiales cuentan la sobrecarga como un tipo de polimorfismo. Tal vez se deba a que para utilizarlo hay que tener claras las ideas de la POO y el diseño de clases por un lado (algo muy difícil si se tiene poca experiencia), y por otro lado los conceptos del manejo de memoria en C++, ya que involucra punteros y conversiones implícitas de tipo. Es decir, ni el diseño ni la sintaxis son triviales. Entonces es lógico que les resulte complejo. El problema es que culpa de esto, le esquivan todo lo posible, y terminan no utilizándolo.
Se puede hacer todo tipo de cosas en C++ sin utilizar polimorfismo, es posible evitarlo, o emularlo. Pero en muchísimos casos, hacerlo de esa manera implica más código, clases más complejas, implementaciones más rebuscadas, más dependencias entre objetos, o el uso de otros trucos tanto o más complicados que el mismo polimorfismo que estaban tratando de evitar. Voy a comentar en este artículo rápidamente de qué se trata, cuales son los mecanismos del compilador que hay por detrás, y qué usos le podemos dar, para que sirva de introducción a otros artículos donde comentaré casos particulares y de interés en PSeInt y en ZinjaI.
Se puede hacer todo tipo de cosas en C++ sin utilizar polimorfismo, es posible evitarlo, o emularlo. Pero en muchísimos casos, hacerlo de esa manera implica más código, clases más complejas, implementaciones más rebuscadas, más dependencias entre objetos, o el uso de otros trucos tanto o más complicados que el mismo polimorfismo que estaban tratando de evitar. Voy a comentar en este artículo rápidamente de qué se trata, cuales son los mecanismos del compilador que hay por detrás, y qué usos le podemos dar, para que sirva de introducción a otros artículos donde comentaré casos particulares y de interés en PSeInt y en ZinjaI.
viernes, 31 de enero de 2014
Sobre la evolución de C++
Aproveché una parte de las vacaciones para tratar de ponerme al día con el "nuevo" estándar de C++: C++11. Digo "nuevo" porque aunque el estándar (el documento que describe el lenguaje) tiene ya 3 años, recién el año pasado los principales desarrolladores de compiladores publicaron versiones con soporte suficientemente estable y completo para las nuevas características y bibliotecas (gcc 4.8, clang 3.3, visual studio 2013, etc). No vengo a explicar las nuevas características, sino que les voy a contar a modo de recomendación de dónde me informé, y que otras cosas muy interesantes me encontré en el camino; para al final tratar de pensar cómo y cuando nos va a afectar esto.
El que me conozca o siga el blog sabe que el 90% (o más) de lo que programo, lo hago con C++. Se convirtió en mi lenguaje favorito por cuestiones más bien pragmáticas, y por oposición a alternativas (discutiblemente) más simples, modernas, o prolijas, pero en general menos eficientes. Estos motivos no hicieron más que afirmarse y crecer durante estas semanas, gracias a algunas lecturas adicionales que encontré en el camino.
El que me conozca o siga el blog sabe que el 90% (o más) de lo que programo, lo hago con C++. Se convirtió en mi lenguaje favorito por cuestiones más bien pragmáticas, y por oposición a alternativas (discutiblemente) más simples, modernas, o prolijas, pero en general menos eficientes. Estos motivos no hicieron más que afirmarse y crecer durante estas semanas, gracias a algunas lecturas adicionales que encontré en el camino.
domingo, 29 de diciembre de 2013
Lecciones de un proyecto no proyectado
Hoy se cumplen 10 años desde que PSeInt fue presentado por primera vez en "público". El 29/12/2003 rendí el final de "Programación I", evaluación que incluía mostrar y defender un programa desarrollado por el alumno con lo aprendido en la materia. No tengo ningún registro exacto de cuándo empecé a codificarlo así que tomo esa presentación formal como fecha de nacimiento. Ni se imaginan lo horrible y lleno de bugs que estaba el código, pero como yo no era consiente de ello, se lo vendí a los docentes como si fuera maravilloso, corriendo sin problemas los ejemplos con los que ya lo había probado. Y como en el poco tiempo que dura el examen no se puede ahondar en tantos detalles, los muchos errores escondidos no saltaron a la vista y me fui con una muy buena nota.
Más allá de eso, jamás imaginé que PSeInt seguiría vivo al día de hoy, y mucho menos que iba a cambiar y evolucionar en la forma en que lo hizo. Siempre le vi patas cortas a este proyecto, y eso me privó de explotarlo un poco más, de hacerlo crecer antes, y de utilizarlo para otras cosas, algo que hoy en algún sentido lamento no haber hecho. De eso vengo a contarles en este post. La historia de un proyecto muy querido pero a la vez subvalorado por su desarrollador. Sobre las veces que dejé pasar oportunidades por falta de imaginación, y cómo el tiempo me demostró que siempre se puede hacer algo más. Espero que este post ayude a salvar algún que otro proyecto con potencial de algún lector, que haya quedado escondido en un cajón por su falta de visión a largo plazo.
Más allá de eso, jamás imaginé que PSeInt seguiría vivo al día de hoy, y mucho menos que iba a cambiar y evolucionar en la forma en que lo hizo. Siempre le vi patas cortas a este proyecto, y eso me privó de explotarlo un poco más, de hacerlo crecer antes, y de utilizarlo para otras cosas, algo que hoy en algún sentido lamento no haber hecho. De eso vengo a contarles en este post. La historia de un proyecto muy querido pero a la vez subvalorado por su desarrollador. Sobre las veces que dejé pasar oportunidades por falta de imaginación, y cómo el tiempo me demostró que siempre se puede hacer algo más. Espero que este post ayude a salvar algún que otro proyecto con potencial de algún lector, que haya quedado escondido en un cajón por su falta de visión a largo plazo.
domingo, 15 de diciembre de 2013
¿Dónde va la gente cuando viene?
Blogger, la plataforma sobre la cual funciona este blog, me ofrece ciertas estadísticas básicas del tráfico del blog. Una de las listas que me ofrece es la de los posts más leídos, diaramente, semanalmente, mensualmente, o en total. Y resulta que el post de este blog más leído de todos los tiempos es uno de los que menos me interesaba. Ni imaginaba que fuera a llamar tanto la atención. Y por otro lado, otros posts que a mí me parecieron mucho más entretenidos o útiles, recibieron relativamente pocas visitas. Les sugiero, si son más o menos seguidores del blog, que traten de recordar cuales posts les llamaron más la atención, o les resultaron más útiles, antes de hacer click en "seguir leyendo" o de pasar al próximo párrafo, para ver si coinciden más conmigo o con las estadísticas.
sábado, 7 de diciembre de 2013
Creación de complementos. Paso 3: empaquetado
Llegamos al últimos de esta serie de posts destinado a mostrar cómo se arman complementos para ZinjaI basados en bibliotecas externas. Vamos a ver a hora cómo utilizar la herramienta que empaqueta los archivos y genera el .zpc que después podemos instalar fácilmente en cualquier ZinjaI. Resumiendo mucho, los pasos que necesarios para llegar hasta aquí fueron:
- Obtener los archivos necesarios para compilar un programa cliente de la biblioteca con MinGW. Esto incluye cabeceras, objetos y dlls. Lograr compilar y ejecutar un ejemplo con estos archivos. Más...
- Armar una carpeta con lo mínimo necesario y modificar la configuración del proyecto para que busque esa carpeta en un ruta relativa a la instalación de ZinjaI. Más...
- Generar el índice de autocompletado, configurarlo en el proyecto, agregar un botón para acceder fácilmente a la referencia, y guardar todo eso como template de proyecto. Más...
lunes, 2 de diciembre de 2013
SFML y sus dependencias
Los cambios en la interfaz binaria de los objetos compilados con MinGW hicieron que deba recompilar algunas bibliotecas externas. Compilar una biblioteca no siempre es fácil en Windows, y encima con SFML me llevé algunas sorpresas interesantes que vengo a comentar aquí.
Las bibliotecas libres suelen tener sus procesos de construcción basados en alguna herramienta libre para tal fin, como pueden ser los scripts de autotools, o la herramienta cmake. Estas herramientas son comunes en GNU/Linux, pero no en Windows (lo común en Windows creo que sería tener un proyecto de Visual Studio). Entonces, como conseguir un entorno apto para estas compilaciones no es tan directo, siempre que podamos conseguir las versiones ya compiladas de las bibliotecas para Windows, mejor (más fácil y más rápido). Es lo que pasó al principio con SFML. Para armar el complemento de sus primeras versiones (1.5 y 1.6), yo no compilé SFML, sino que bajé la versión compilada que se ofrece en su sitio oficial y le agregué lo necesario para utilizarla en ZinjaI (como las plantillas de proyectos ya configurados y los indices de autocompletado).
Las bibliotecas libres suelen tener sus procesos de construcción basados en alguna herramienta libre para tal fin, como pueden ser los scripts de autotools, o la herramienta cmake. Estas herramientas son comunes en GNU/Linux, pero no en Windows (lo común en Windows creo que sería tener un proyecto de Visual Studio). Entonces, como conseguir un entorno apto para estas compilaciones no es tan directo, siempre que podamos conseguir las versiones ya compiladas de las bibliotecas para Windows, mejor (más fácil y más rápido). Es lo que pasó al principio con SFML. Para armar el complemento de sus primeras versiones (1.5 y 1.6), yo no compilé SFML, sino que bajé la versión compilada que se ofrece en su sitio oficial y le agregué lo necesario para utilizarla en ZinjaI (como las plantillas de proyectos ya configurados y los indices de autocompletado).
viernes, 29 de noviembre de 2013
Creación de complementos. Paso 2: extras
Ya tenemos un proyecto que anda, y configurado de forma que solo copiando un par de carpetas de un ZinjaI a otro podemos replicarlo. En esta tercera parte vamos a pulir algunos detallecitos y a generar el bendito índice de autocompletado que hará que ZinjaI nos ayude a utilizar las clases y funciones de la biblioteca mientras escribimos el programa cliente.
Empecemos por generar el índice. Lo vamos a hacer automáticamente a partir de las cabeceras (los .h y .hpp). Esto no siempre es lo mejor, porque a veces tienen definidas cosas demás, que no nos interesan para el autocompletado (como las guardas), o porque a veces no se condicen con la documentación. Digamos por ejemplo que para una clase Ventana la documentación dice que va "#include <Ventana.h>", pero en realidad Ventana.h no tiene la clase sino un montón de #ifs que según el sistema operativo hacen un #include diferente con la versión de la clase para cada sistema operativo. El parseo automático dirá por ejemplo que Ventana está en <Win32/Ventana.h> mientras que lo correcto es poner solo <Ventana.h> o de lo contrario no compilará en otro sistema. Como este hay muchos casos, y por eso los índices de wxWidgets y SFML-1 se generaron parseando los archivos de la referencia HTML y no las cabeceras. Pero este parseo es ad-hoc y requiere mucho trabajo (y programar), así que vamos por la forma fácil y esperemos que no traiga demasiados problemas.
Empecemos por generar el índice. Lo vamos a hacer automáticamente a partir de las cabeceras (los .h y .hpp). Esto no siempre es lo mejor, porque a veces tienen definidas cosas demás, que no nos interesan para el autocompletado (como las guardas), o porque a veces no se condicen con la documentación. Digamos por ejemplo que para una clase Ventana la documentación dice que va "#include <Ventana.h>", pero en realidad Ventana.h no tiene la clase sino un montón de #ifs que según el sistema operativo hacen un #include diferente con la versión de la clase para cada sistema operativo. El parseo automático dirá por ejemplo que Ventana está en <Win32/Ventana.h> mientras que lo correcto es poner solo <Ventana.h> o de lo contrario no compilará en otro sistema. Como este hay muchos casos, y por eso los índices de wxWidgets y SFML-1 se generaron parseando los archivos de la referencia HTML y no las cabeceras. Pero este parseo es ad-hoc y requiere mucho trabajo (y programar), así que vamos por la forma fácil y esperemos que no traiga demasiados problemas.
sábado, 23 de noviembre de 2013
Creación de complementos. Paso 1: relatividad
En el post anterior conté algo sobre algunos problemas que aparecían al querer utilizar bibliotecas externas en un proyecto de ZinjaI, y expliqué la solución a un ejemplo paso por paso. El ejemplo se basaba en la biblioteca OpenCV en su versión para Windows. Si siguieron más o menos esos lineamientos, deberían poder compilar un programa básico con OpenCV y verlo corriendo. En ese caso tuvimos que, además de configurar el proyecto, compilar la biblioteca.
En este segundo post vamos a reacomodar esos archivos de la biblioteca, para tener lo justo y necesario en una sola carpeta, carpeta que estará dentro de la carpeta de instalación de ZinjaI para evitar andar desparramando más archivos por el sistema, y para que además podamos llevarla a cualquier otra PC y sin importar dónde se halla instalado ZinjaI en esa otra PC, el proyecto compile igual sin cambios. Todavía se podrán ajustar más detalles muy útiles (como el autocompletado por ejemplo) antes de tener todo listo para armar finalmente la plantilla y el complemento, pero eso quedará para una tercera parte.
En este segundo post vamos a reacomodar esos archivos de la biblioteca, para tener lo justo y necesario en una sola carpeta, carpeta que estará dentro de la carpeta de instalación de ZinjaI para evitar andar desparramando más archivos por el sistema, y para que además podamos llevarla a cualquier otra PC y sin importar dónde se halla instalado ZinjaI en esa otra PC, el proyecto compile igual sin cambios. Todavía se podrán ajustar más detalles muy útiles (como el autocompletado por ejemplo) antes de tener todo listo para armar finalmente la plantilla y el complemento, pero eso quedará para una tercera parte.
martes, 19 de noviembre de 2013
Creación de complementos. Paso 0: configurar un proyecto
Alguien en el foro preguntó cómo hacer para utilizar OpenCV (una biblioteca con todo tipo de utilidades para cuestiones relacionadas a
la visión computacional y bastante popular por estos días) en ZinjaI y en Windows. Alguna vez ya escribí de forma genérica acerca de cómo configurar un proyecto en ZinjaI para utilizar una biblioteca. Eso sigue siendo bastante cierto, pero el detalle del cambio de ABI del compilador que comenté en este otro post complicó las cosas. Y aún sin ese problema, las instrucciones aquellas son solo para compilar, pero nos vendría bien también armar una plantilla de proyecto, tener un índice de autocompletado, tal vez acceso a la referencia, etc. Entonces hoy voy a tomar el caso de OpenCV como ejemplo y contar paso a paso cómo armar todo eso y empaquetarlo en un complemento para instalar en ZinjaI en dos pasos. Espero que con este ejemplo los usuarios más interesados se animen a amar sus propios templates, autocompletados, o hasta complementos para compartir con los demás usuarios de ZinjaI.
martes, 8 de octubre de 2013
Exportar un pseudocódigo a un lenguaje real
Desde hace muchísimo tiempo, PSeInt tiene la opción para exportar un pseudocódigo a C++. Es decir, para generar un código C++ que haga exactamente lo mismo que hace un algoritmo en pseudocódigo al ejecutarse en PSeInt. No siempre es posible, pero en general se puede obtener una buena aproximación. El objetivo de esta funcionalidad es bastante dudoso. Definitivamente no está pensado para que generen a través de PSeInt programas reales (es decir, para no tener que aprender un lenguaje real, sino hacerlo acá y convertirlo). Está pensado para servir como ayuda cuando el estudiante empieza a aprender un lenguaje real, luego de haber adquirido lo básico mediante el pseudocódigo. Para generar una sintonía entre lo que ya conoce y lo que está aprendiendo.
El único motivo por el cual se convierte a C++ y no a otro lenguaje es porque C++ es el lenguaje que domino bien, en el cual me siento cómodo, y del cual creo conocer lo suficiente como para decidir cómo conviene hacer las traducciones. Pero muchos docentes que utilizan PSeInt para comenzar, luego pasan a otros lenguajes, y me gustaría que PSeInt también pudiera exportar a muchos de esos otros. En este post voy a contar un poco cómo espero lograrlo, solicitando la colaboración de los docentes (o usuarios en general) que tengan gran conocimiento de esos otros lenguajes para proporcionar ejemplos, y voy a hablar también de los problemas y desafíos que esta traducción supone.
El único motivo por el cual se convierte a C++ y no a otro lenguaje es porque C++ es el lenguaje que domino bien, en el cual me siento cómodo, y del cual creo conocer lo suficiente como para decidir cómo conviene hacer las traducciones. Pero muchos docentes que utilizan PSeInt para comenzar, luego pasan a otros lenguajes, y me gustaría que PSeInt también pudiera exportar a muchos de esos otros. En este post voy a contar un poco cómo espero lograrlo, solicitando la colaboración de los docentes (o usuarios en general) que tengan gran conocimiento de esos otros lenguajes para proporcionar ejemplos, y voy a hablar también de los problemas y desafíos que esta traducción supone.
Suscribirse a:
Entradas (Atom)