viernes, 5 de junio de 2009

HAL y la burocracia (X - redundancia)

Si recurríamos a los bucles para evitar la repetición innecesaria del mismo código sobre los elementos de una lista de datos, carece de sentido mantener en el interior del bucle construcciones redundantes. Y nuestra última redacción posee un grado de redundancia intolerable:

...
for linea in $(cat $NOTAS)
do
if [ $(echo $linea | cut -d':' -f3) -lt $NOTA_MINIMA ]
then
# El nombre del alumno y curso con el formato que tendrán en la salida impresa
ALUMNO_OUTPUT=$(echo $linea | cut -d':' -f1 | sed -e 's/-/ /g')
CURSO_OUTPUT=$(echo $linea | cut -d':' -f2 | sed -e 's/\([[:digit:]]*\)-\(.*\)/\1\.º \2/' \
-e 's/ge/EE/' \
-e 's/gm/EP/')

# Las propuestas de trabajo se deben introducir interactivamente
echo "Introduzca propuestas de trabajo para $ALUMNO_OUTPUT [\item ... (^D para salir)]: "
cat > trabajo_tmp.tex

sed -e "s/ALUMNO/$ALUMNO_OUTPUT/" \
-e "s/CURSO/$CURSO_OUTPUT/" \
-e "s/OBJETIVOS/\\\input{$(echo $linea | cut -d':' -f2)-${OBJETIVOS}}/" \
-e "s/CONTENIDOS/\\\input{$(echo $linea | cut -d':' -f2)-${CONTENIDOS}}/" \
-e 's/RECUPERACIÓN/\\input{trabajo_tmp}/' $PLANTILLA > $(echo $linea | cut -d':' -f1).tex

pdflatex $(echo $linea | cut -d':' -f1).tex
fi
done

Una forma de evitar estas inoperantes y fastidiosas redundancias es convertir el código que se repite en el valor de una variable. Con ello logramos una serie de beneficios claros:

  • El código en cuestión se ejecuta una única vez, en lugar de varias, lo que implica un uso más eficiente de los recursos.

  • La complejidad disminuye, porque donde había una larga tubería habrá ahora un simple nombre de variable.

  • El resultado es más manejable, puesto que si se decide modificar el guión, bastará hacerlo en un solo sitio y no en varios.

  • El resultado es más legible, pues el nombre de la variable, si está bien elegido, será mucho más fácilmente comprensible que la cadena de órdenes por la que está.


Procedamos a empaquetar el código que extrae el primer y segundo campo de cada línea del fichero $NOTAS, nombre de alumno y curso, respectivamente, en una variable adecuada:

# Nombre y curso del alumno tal como constan en el fichero $NOTAS
ALUMNO=$(echo $linea | cut -d':' -f1)
CURSO=$(echo $linea | cut -d':' -f2)

El siguiente paso es incorporar las definiciones de estas variables y modificar las líneas del guión que contenían lo que ahora es el valor de tales variables. Recordemos, por otra parte, que cuando el valor de una variable se ha de manipular dentro de una tubería por un filtro que exige como entrada una cadena de caracteres se lo pasamos como argumento a echo:

for linea in $(cat $NOTAS)
do
if [ $(echo $linea | cut -d':' -f3) -lt $NOTA_MINIMA ]
then
# Nombre y curso del alumno tal como constan en el fichero $NOTAS
ALUMNO=$(echo $linea | cut -d':' -f1)
CURSO=$(echo $linea | cut -d':' -f2)

# El nombre del alumno y curso con el formato que tendrán en la salida impresa
ALUMNO_OUTPUT=$(echo $ALUMNO | sed -e 's/-/ /g')
CURSO_OUTPUT=$(echo $CURSO | sed -e 's/\([[:digit:]]*\)-\(.*\)/\1\.º \2/' \
-e 's/ge/EE/' \
-e 's/gm/EP/')

# Las propuestas de trabajo se deben introducir interactivamente
echo "Introduzca propuestas de trabajo para $ALUMNO_OUTPUT [\item ... (^D para salir)]: "
cat > trabajo_tmp.tex

sed -e "s/ALUMNO/$ALUMNO_OUTPUT/" \
-e "s/CURSO/$CURSO_OUTPUT/" \
-e "s/OBJETIVOS/\\\input{${CURSO}-${OBJETIVOS}}/" \
-e "s/CONTENIDOS/\\\input{${CURSO}-${CONTENIDOS}}/" \
-e 's/RECUPERACIÓN/\\input{trabajo_tmp}/' $PLANTILLA > ${ALUMNO}.tex

pdflatex ${ALUMNO}.tex
fi
done

Veamos qué dice diff si guardamos esta nueva versión con el nombre generar_informes-3:

diff -Bbi -u generar_informes-2 generar_informes-3

Su respuesta es la siguiente:

--- generar_informes-2 2009-06-05 15:20:33.000000000 +0200
+++ generar_informes-3 2009-06-05 21:38:40.000000000 +0200
@@ -20,9 +20,13 @@
do
if [ $(echo $linea | cut -d':' -f3) -lt $NOTA_MINIMA ]
then
+ # Nombre y curso del alumno tal como constan en el fichero $NOTAS
+ ALUMNO=$(echo $linea | cut -d':' -f1)
+ CURSO=$(echo $linea | cut -d':' -f2)
+
# El nombre del alumno y curso con el formato que tendrán en la salida impresa
- ALUMNO_OUTPUT=$(echo $linea | cut -d':' -f1 | sed -e 's/-/ /g')
- CURSO_OUTPUT=$(echo $linea | cut -d':' -f2 | sed -e 's/\([[:digit:]]*\)-\(.*\)/\1\.º \2/' \
+ ALUMNO_OUTPUT=$(echo $ALUMNO | sed -e 's/-/ /g')
+ CURSO_OUTPUT=$(echo $CURSO | sed -e 's/\([[:digit:]]*\)-\(.*\)/\1\.º \2/' \
-e 's/ge/EE/' \
-e 's/gm/EP/')

@@ -32,11 +36,10 @@

sed -e "s/ALUMNO/$ALUMNO_OUTPUT/" \
-e "s/CURSO/$CURSO_OUTPUT/" \
- -e "s/OBJETIVOS/\\\input{$(echo $linea | cut -d':' -f2)-${OBJETIVOS}}/" \
- -e "s/CONTENIDOS/\\\input{$(echo $linea | cut -d':' -f2)-${CONTENIDOS}}/" \
- -e 's/RECUPERACIÓN/\\input{trabajo_tmp}/' $PLANTILLA > $(echo $linea | cut -d':' -f1).tex
+ -e "s/OBJETIVOS/\\\input{${CURSO}-${OBJETIVOS}}/" \
+ -e "s/CONTENIDOS/\\\input{${CURSO}-${CONTENIDOS}}/" \
+ -e 's/RECUPERACIÓN/\\input{trabajo_tmp}/' $PLANTILLA > ${ALUMNO}.tex

- pdflatex $(echo $linea | cut -d':' -f1).tex
+ pdflatex ${ALUMNO}.tex
fi
done


Resumen

  • Por regla general, las redundancias deben ser evitadas en la escritura de guiones o programas.

No hay comentarios:

Publicar un comentario