作者uranusjr (←这人是超级笨蛋)
看板Python
标题Re: [问题] 如何比较list里面各项数字的大小并重组?
时间Thu Aug 1 11:37:17 2013
1F:→ SocketAM2:u = [min(s[i], t[i]) for i in range(len(s))] 08/01 01:35
2F:→ SocketAM2:我发现我的第一感好像跟上面的各位很不一样...... 08/01 01:36
3F:→ SocketAM2:有大大可以点评一下大家的第一感的优劣之处吗? 08/01 01:36
4F:→ SocketAM2:个人觉得mikapauli的"list(map(min, s, t))"是目前最优 08/01 01:37
5F:→ SocketAM2:除了简洁(不熟map就不直觉XD)外,我猜执行效率是最优的 08/01 01:39
6F:→ SocketAM2:看了下mikapauli大在原PO上一篇的推文自以为效率也很高 08/01 01:42
直觉这种东西本来就很主观, 重点是不能为了直觉牺牲太多效率
需要会最少东西的写法应该是
result = []
for i in range(0, len(s)): # 阵列很长时应改用 xrange
if s[i] > t[i]:
result.append(t[i])
else:
result.append(s[i])
当然这基本上是 O(n) 所以大方向对了, 不过还是有几个问题
1. 做了 len(s) 次 index lookups
Subscript syntax 基本上是做 random access, 速度不如 iterator
当次数大时效率会有明显落差
2. 对不等长阵列的支援不好, 如果要加上更多判断, 可读性会慢慢下降
3. 出来的东西只能是阵列, 如果你之後还要 iterate 它, 等於做了白工多转换一次
4. 扩充性不佳, 例如如果以後要多比较一个阵列 r 就会炸裂
针对最後一点, 所以我们必须学习内建的 min 和 max
这里可以用 min 来改写
result = []
for i in range(len(s)): # 只给一个参数就预设是从 0 开始
result.append(min(s[i], t[i]))
事实上我觉得即使初学者, 最低限度也要写成这样, 更差的都不及格
第三点可以改成 generator 或输出 iterator 来解决
例如包成 generator...
def generate_mins(list1, list2):
for i in range(len(list1)):
yield min(list1[i], list2[i])
result = generate_mins(s, t)
不过 yield 算进阶语法, 为了不用更常用的语法而先学是本末倒置, 所以跳过
另外一种产生方法就等於要学 list comprehension 了
result = (min(s[i], t[i]) for i in range(0, len(s)))
只差在有没有在这里直接转成 list (改用方括弧而非原括弧包起来) 而已
第二点稍後讨论, 先讲第一点
写资料处理的程式时, iteration 的速度基本上决定了你的程式价值
即使再怎麽优美或容易维护或怎样, 你处理速度慢人家超过一定程度就等於垃圾
所以各种语言才会用各式各样的方法为非连续记忆体的 list loop-through 加速
Python (和其他很多语言) 的做法就是使用 foreach 语法
写 for i in a_list: i 效率就是会优於 for i in range(a_list): a_list[i]
所以我们要做的是想办法同时 iterate 复数个 lists, 而不是改用 suubscription
Python 提供的解决方案就是 zip 与自动 tuple 展开
所以继续改写...
result = (min(i[0], i[1]) for i in zip(s, t))
自动展开
result = (min(a, b) for a, b in zip(s, t))
这样就有基本的样子了
或者 如果你了解到其实 min 其实可以直接吃 iterable...
result = (min(i) for i in zip(s, t))
关於第二点, zip 有一个额外性质是会自动 trim-up
当输入的 lists 不等长, 会自动把多的丢掉
如果你不喜欢这个行为, 而希望当某个 list 不够长时提供预设值
则可以使用 itertools module 里的 izip_longest
from itertools import izip_longest
result = (min(i) for i in izip_longest(s, t, float('inf')))
float('inf') 会产生「无穷大」, 所以这等於如果有阵列不够长就无视它
顺带一题, 如果输入阵列太长, zip 也有可能会有速度问题
这时候可以改用 itertools 的 izip
这样我们就有了一个效率很好, 可读性也优良的写法
大部分人也都是这样推荐
======
前面有人提出 map 的写法
map(min, zip(s, t, u))
这也合理, 不过 map 在 Python 3 的行为有改, 要注意
Python 基本上比较不喜欢这类 function-oriented 的语法
这是 Guido 的个人偏好...
--
「我最想要的同伴嘛,首先是要笑口常开,其次是我们能永远不会发生误会。
如果这些都能办到的话,嗯,如果他是世界上第一流的桥手,也还不错。」
-- 班尼多‧加罗素,前义大利蓝队成员
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 140.112.94.61
7F:推 tiefblau:好文推 08/01 11:48
8F:→ drm343:python 的 map 跟 scheme 相同,zip 是不需要的 08/01 12:08
9F:推 vvind:受教了 08/01 12:40
10F:推 MAGICXX:推这篇! 08/01 12:51
11F:推 lc85301:这种有历史跟程式概念的东西要从哪里学lol 08/01 12:53
12F:推 TuTu522:推 08/01 14:03
※ 编辑: uranusjr 来自: 140.112.94.61 (08/01 14:42)
13F:推 timTan:Push~~ 08/01 17:01
14F:推 timTan:历史的部份,有爱就会有阿~ 08/01 17:12
15F:→ timTan:有爱自然会常常看 Relase Note 跟 PEP, 就会知道很多事情。 08/01 17:12