lunes, 12 de diciembre de 2011

Serie numérica

Hace un tiempo en un pizarrón en la oficina apareció la siguiente serie:

1
11
21
1211
111221
312211
13112221

1113213211

Algunas preguntas:
  1. ¿Cuál es el próximo elemento de la serie y como se construye?
  2. ¿Cuál es el dígito más grande que puede aparecer? Tip: es menor que 9 :)
  3. Tengo una conjetura: el largo de un elemento de la serie no puede ser menor que el largo del elemento anterior. ¿Alguien se anima a demostrarlo?

sábado, 10 de diciembre de 2011

Sobre la comparación entre lenguaje de programación

El otro día en un almuerzo salió el tema de si tiene sentido comparar distintos lenguajes de programación.

Una de las posturas era que en realidad, con cualquier lenguaje de programación moderno (léase Java, C#, Objective-C, Ruby, etc.) puedo solucionar cualquier problema que pueda solucionar con otro de estos lenguajes.

De todas formas yo creo que sí se pueden comparar, y que si bien con cualquiera se pueden resolver los problemas, no se obtiene la misma productividad.

Voy a poner un ejemplo, usando una muy pequeña característica de los lenguajes de programación: los loops de tipo "for".

El caso más simple, es querer recorrer una colección partiendo del primer item hasta el último, avanzando de a uno. Para eso me alcanza una construcción del tipo:

for index = 0 to count-1 {
  // hacer algo con index
}


¿Qué pasa ahora si quiero contar de dos en dos? ¿Me sirve la construcción anterior? Bueno, sí... puedo escribir lo siguiente:

for fakeIndex = 0 to (count-1)/2 {
  index = fakeIndex * 2
  // hacer algo con index
}


No es complicado, pero no parece muy práctico. Un lenguaje que le pueda decir el "step" al comando "for" no es más potente, pero sí más práctico. La forma de escribirlo quedaría entonces:

for index = 0 to count-1 step 2 {
  // hacer algo con index
}


El problema que tiene esto, es que si quiero que el step sea variable, por ejemplo quiero iterar sobre las potencias de 2, con el step solo no me alcanza. Lo puedo resolver, pero queda más complicado. Una solución es usar la sintaxis que usa C, donde se le indica el valor inicial, la condición de parada, y el incremento. Así:

for (int index = 1; index <= someValue; index *= 2) {
  // hacer algo con index
}


Esta iteración sí la puedo construir con el primer tipo de "for", pero es más difícil: hay que pensar como mapear el índice de la iteración con el índice que realmente quiero manejar.

De todas formas, cuando uno tiene este "for" como es el caso de C y todos sus lenguajes derivados, lo más común es usarlo para acceder a los elementos de una colección:

for (int index = 0; index < array.count; index++) {
  value = array[index];
  // hacer algo con value
}


En general, el índice se termina usando solo para acceder al elemento correspondiente de la colección. El valor inicial, el final y el incremento son una construcción artificial. Lo que realmente quiero es poder iterar sobre los elementos de mi colección.

Supongamos ahora que tengo una construcción que me permite hacer esto:

for (value in array) {
  // hacer algo con value
}


¿Es esta construcción más potente que la anterior? No, las dos permiten expresar lo mismo. Pero sin duda es mucho más conveniente, aumenta la productividad y minimiza el riesgo de cometer errores (por ejemplo si uso "index <= array.count" en vez de "index < array.count", error bastante común).

¿Se puede hacer mejor? Sí, se puede, y lo curioso del caso es que para la forma de mejorarlo es eliminar la construcción "for", que es lo que se tiene en Ruby (donde sí existe el "for" pero no se usa...).

Si no tengo el comando "for", entonces tengo que buscar otra forma de iterar sobre una colección o conjunto de valores. Esto en Ruby se resuelve con el método "each" (o "each_index") de la siguiente forma:

array.each do |value|
  # hacer algo con value
end


o

(0..5).each do |index|
  # hacer algo con index
end


¿Por qué esto es mejor? Porque si el "each" es un método y no una construcción del lenguaje, quiere decir que el lenguaje me permite iterar de la forma que quiera. Ahora puedo separar el uso del "for" según para que lo quiera.

Las cosas más comunes para las que recorro una colección son para transformar los elementos en otra colección, filtrar los valores según alguna condición, o acumular en una variable. Entonces puedo tener métodos que hagan estas cosas, y en mi programa no voy a necesitar la iteración, porque en realidad lo que quería no era iterar sino hacer algo de más alto nivel a la colección.

En conclusión, es verdad que no hay nada que no pueda resolver con las construcciones más simples, y por lo tanto no hay un lenguaje más potente que otro, lo que sí hay son lenguajes más productivos.


Actualización, 12/12/11 8:56: Willy me encontró un bug, así que lo corregí... Había un loop que empezaba en cero e incrementaba multiplicando por dos, por lo que nunca iba a salir...