18.6. 优化字符串操作

Soundex 算法的最后一步是对短结果补零和截短长结果。最佳的做法是什么?

这是目前在 soundex/stage2/soundex2c.py 中的做法:

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

这里是 soundex2c.py 的表现:

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

思考的第一件事是以循环取代正则表达式。这里的代码来自 soundex/stage4/soundex4a.py

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

soundex4a.py 快了吗?是的:

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

但是,等一下。一个从字符串去除字符的循环?我们可以用一个简单的字符串方法做到。这便是 soundex/stage4/soundex4b.py

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

soundex4b.py 快了吗?这是个有趣的问题,它取决输入值:

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

soundex4b.py 中的字符串方法对于大多数名字比循环快,但是对于短小的情况 (很短的名字) 却比 soundex4a.py 略微慢些。性能优化并不总是一致的,对于一个情况快些,却可能对另外一些情况慢些。就此而言,大多数情况将会从改变中获益,所以就改吧,但是别忘了原则。

最后仍很重要的是,让我们检测算法的最后两步:以零补齐短结果和截短超过四字符的长结果。你在 soundex4b.py 中看到的代码就是做这个工作的,但是太没效率了。看一下 soundex/stage4/soundex4c.py 找出原因:

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

我们为什么需要一个 while 循环来补齐结果?我们早就知道我们需要把结果截成四字符,并且我们知道我们已经有了至少一个字符 (直接从 source 中拿过来的起始字符)。这意味着我们可以仅仅在输出的结尾添加三个零,然后截断它。不要害怕重新理解问题,从不太一样的角度看问题可以获得简单的解决方案。

我们丢弃 while 循环后从 soundex4c.py 中获得怎样的速度?太明显了:

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

最后,还有一件事可以令这三行运行得更快:你可以把它们合并为一行。看一眼 soundex/stage4/soundex4d.py

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

soundex4d.py 中把所有代码放在一行可以比 soundex4c.py 稍微快那么一点:

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

它非常难懂,而且优化也不明显。这值得吗?我希望你有很好的见解。性能并不是一切。你在优化方面的努力应该与程序的可读性和可维护性相平衡。