Programs must be written for people to read, and only incidentally for machines to execute.
Abelson & Sussman, Structure and Interpretation of Computer Programs
Todo el código que hagamos será leído, en algún momento, por otra persona. Incluso si somos los únicos en mirar nuestro código, los mismos que cuando lo escribimos. No estamos pensando lo mismo que entonces, y no lo conocemos igual.
Siempre tenemos que escribir código de la manera más legible posible, y sólo cuando se demuestra que la claridad interfiere con la velocidad Y la velocidad es importante, podríamos pensar en reescribir.
Si tenemos estructuras que podemos reconocer, y las aplicamos a nuestro código, será mucho más rápida la lectura, porque podemos reconocer de un vistazo qué partes son importantes.
80 caracteres por línea
En tiempos anteriores esta regla nació de la incapacidad de las terminales de mostrar líneas muy anchas. Muchos piensan que, dado que ya no es problema, es una regla irrelevante, pero se equivocan: El número mágico es 7 ± 2. Esto significa que se puede tener en memoria inmediata entre 5 y 9 elementos. Esto nos da una longitud promedio de nombre de 9 a 16 caracteres; descontando espacios, comas, paréntesis y demás, nos quedan cerca de 6 a 10 caracteres por nombre.
Al ingresar en un bucle, o en un condicional, indentaremos lo que pongamos adentro. Recordar que estamos en un bucle gasta uno de los espacios en memoria, así que tenemos menos espacio para la línea.
Las líneas cortas son más fáciles de entender.
Múltiples puntos de salida
Algunos lenguajes (como Pascal) nos lo prohíben, pero tener múltiples puntos de salida de una función pueden acortarlas y reducir la indentación:
def alternar(lista1, lista2): retorno = [] if(len(lista1) > 0): if(len(lista2) > 0): retorno = [lista1[0], lista2[0]] + alternar(lista1[1:],lista2[1:]) else: retorno = lista1 else: retorno = lista2 return retorno
puede ser reescrito, mucho más legible, como:
def alternar(lista1, lista2): if(len(lista1) == 0): return lista2 if(len(lista2) == 0): return lista1 return [lista1[0], lista2[0]] + alternar(lista1[1:],lista2[1:])
Primero nos deshacemos de los casos especiales, y finalmente actuamos sobre el caso recursivo.
En una función encargada del login, por ejemplo, sólo actuamos si el nombre de usuario es válido—o, si usamos múltiples puntos de salida, salimos inmediatamente al encontrar un usuario inválido.
Sintaxis significativa
Nada impide usar un for, y a veces es lo mejor. Reg Braithwaite nos dice que la sintaxis es una señal: si queremos aplicar una función sobre cada elemento de una lista, y nos interesa el valor de retorno, es mejor usar un map; si nos importan los efectos colaterales, es mejor un bucle.
Estructuras poderosas
El lambda es hermoso. Funciones anónimas, como objetos de primera clase, clausuras, y otros conceptos de la programación funcional, que podemos encontrar en Python, Ruby, Lisp, y más, son más que útiles para aprender, o como diría Eric Raymond: Lisp is worth learning for the profound enlightenment experience you will have when you finally get it; that experience will make you a better programmer for the rest of your days, even if you never actually use Lisp itself a lot.
.
La mejor forma de mejorar (valga la redundancia) es salir de lo que estamos acostumbrados, aprender un concepto nuevo, y resolver un problema—quizá uno que ya hayamos resuelto— con lo que acabamos de aprender.
Implementá una lista enlazada simple en un lenguaje orientado a objetos, usando sólo una llamada a un método por línea. Calculá el dígito de verificación de un CUIT con map y reduce.
Y si mientras lo hacés aprendés un lenguaje nuevo, grokkeas el concepto que acabás de aplicar, y te gusta, recién ahí aprendiste a programar.