最近,我在处理一个文本文件,它有三列,分别为文件名,数字,字符串,且其中第一列中有若干重复了的部分,于是我需要把这些重复的行合并起来,即第二列合并为总和,第三列合并为一条新字符串,且用空格分隔。

我一开始想如此合并:

1
2
3
4
5
sum = 0
string = ''
for duplicate_line in duplicate_lines:
sum += int(duplicate_line.split()[1])
string = ' '.join(string, duplicate_line.split()[2])

但迭代完后 string 并不符合我的期望,它包含了一个 heading space! 这令我想起了抽象代数学上的「单位元」,即它在某二元运算中,与任意元素运算后,结果的值恒为后者本身。比如加法中的 0 与任何数字相加,恒返回后者的原值;乘法中的 1 也同理。

于是我们可以说,把 ‘ ‘.join() 当二元运算符来看时,不存在单位元,即 ‘ ‘.join([a, b]) 中 b 为任意字符串时,不存在 a 变量能使这函数返回 b 本身,哪怕变量 a 为空字符串时,也会返回包含了一个 heading space 和 b 值的新字符串!就像我上面的代码一样。不过 ‘’.join() 当然就有单位元即空字符串。

反观 Python 的 int 加法,它就有单位元,即 sum 的初值 0, 也难怪它和悲剧的 string 君不同,可以安全地返回我期望的值。

这启发了我又一条编程规范:要小心那些没有单位元的函数或运算符,并处理得当。其实上面合并 string 的代码可以妙用 list comprehension, 回避掉 heading space.

1
' '.join([duplicate_line.split()[2] for duplicate_line in duplicate_lines])

举一反三,Python Sequences 的 index 为什么从 0 而不是 1 开始计呢?您倒不如想想另一个问题:Sequences Index 的单位元是什么?即对 Index 进行二元运算时,加多少就返回原 Index? 当然是 0 了!再看 C 指针,它和 0 相加时就返回原指针,也难怪 C 的 Array Index 和 Python 的 Sequences Index 都从 0 开始,这是为了当直接把原 Index(甚至 C 的指针即地址)和 Index 初始值(即单位元)相加时,能方便且直接地得到原 Index(甚至 C 的原指针即原地址)

Comments

2017 Sep 4