18.6. Optimización de manipulación de cadenas

El último paso del algoritmo Soundex es rellenar los resultados cortos con ceros y truncar los largos. ¿Cual es la mejor manera de hacerlo?

Esto es lo que tenemos hasta ahora, tomado de soundex/stage2/soundex2c.py:

    digits3 = re.sub('9', '', digits2)
    while len(digits3) < 4:
        digits3 += "0"
    return digits3[:4]

Éstos son los resultados de soundex2c.py:

C:\samples\soundex\stage2>python soundex2c.py
Woo             W000 12.6070768771
Pilgrim         P426 14.4033353401
Flingjingwaller F452 19.7774882003

La primera cosa que hemos de considerar es sustituir esa expresión regular con un bucle. Este código es de soundex/stage4/soundex4a.py:

    digits3 = ''
    for d in digits2:
        if d != '9':
            digits3 += d

¿Es más rápido soundex4a.py? Pues sí:

C:\samples\soundex\stage4>python soundex4a.py
Woo             W000 6.62865531792
Pilgrim         P426 9.02247576158
Flingjingwaller F452 13.6328416042

Pero espere un momento. ¿Un bucle para eliminar caracteres de una cadena? Podemos usar un simple método de cadenas para eso. Aquí está soundex/stage4/soundex4b.py:

    digits3 = digits2.replace('9', '')

¿Es más rápido soundex4b.py? Ésa es una pregunta interesante. Depende de la entrada:

C:\samples\soundex\stage4>python soundex4b.py
Woo             W000 6.75477414029
Pilgrim         P426 7.56652144337
Flingjingwaller F452 10.8727729362

El método de cadena de soundex4b.py es más rápido que el bucle en la mayoría de los casos, pero es ligeramente más lento que soundex4a.py en el caso trivial (de un nombre muy corto). Las optimizaciones de rendimiento no son siempre uniformes; el afinamiento que hace un caso más rápido a veces puede hacer más lentos otros casos. En este caso, la mayoría se beneficia del cambio, así que lo dejaremos como está, pero es un principio importante de recordar.

Por último pero no por ello menos importante, examinemos los dos pasos finales del algoritmo: rellenar los resultados cortos con ceros y truncar los resultados largos a cuatro caracteres. El código que vimos en soundex4b.py hace eso mismo, pero es horriblemente ineficiente. Eche un vistazo a soundex/stage4/soundex4c.py para ver por qué:

    digits3 += '000'
    return digits3[:4]

¿Par qué necesitamos un bucle while sólo para rellenar el resultado? Sabemos con antelación que vamos a truncar el resultado a cuatro caracteres, y ya sabemos que tenemos al menos un carácter (la letra inicial, que llega sin cambiar de la variable source original). Eso significa que podemos añadir simplemente tres ceros a la salida y luego truncarla. No se ciña totalmente al enunciado exacto del problema; verlo desde un ángulo ligeramente diferente puede llevar a una solución más simple.

¿Cuánta velocidad ganamos en soundex4c.py eliminando el bucle while? Es significativa:

C:\samples\soundex\stage4>python soundex4c.py
Woo             W000 4.89129791636
Pilgrim         P426 7.30642134685
Flingjingwaller F452 10.689832367

Por último, aún hay una cosa que podemos hacer con esas tres líneas de código para acelerarlas: podemos combinarlas en una sola. Eche un vistazo a soundex/stage4/soundex4d.py:

    return (digits2.replace('9', '') + '000')[:4]

Poner todo este código en una sola línea en soundex4d.py es ligeramente más rápido que soundex4c.py:

C:\samples\soundex\stage4>python soundex4d.py
Woo             W000 4.93624105857
Pilgrim         P426 7.19747593619
Flingjingwaller F452 10.5490700634

También es bastante menos legible y no gana mucha velocidad. ¿Merece la pena? Espero que haya puesto buenos comentarios. El rendimiento no lo es todo. Sus esfuerzos de optimización siempre deben estar equilibrados frente a las amenazas a la legibilidad y mantenibilidad de los programas.