Pyt*_*nic 27 python iteration algorithm list
给出一个清单
a = [0,1,2,3,4,5,6,7,8,9]
Run Code Online (Sandbox Code Playgroud)
我怎样才能得到
b = [0,9,1,8,2,7,3,6,4,5]
Run Code Online (Sandbox Code Playgroud)
也就是说,产生一个新的列表,其中每个连续的元素交替地从原始列表的两边取出?
Nor*_*man 50
>>> [a[-i//2] if i % 2 else a[i//2] for i in range(len(a))]
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
Run Code Online (Sandbox Code Playgroud)
说明:
此代码从一开始就挑选号码(a[i//2]
)和结束(a[-i//2]
的)a
,交替(if i%2 else
).选择了总数len(a)
,因此即使len(a)
是奇数,也不会产生任何不良影响.
[-i//2 for i in range(len(a))]
产率0, -1, -1, -2, -2, -3, -3, -4, -4, -5
,
[ i//2 for i in range(len(a))]
产量0, 0, 1, 1, 2, 2, 3, 3, 4, 4
,
和i%2
之间交替False
和True
,
所以我们从提取的索引a
是:0, -1, 1, -2, 2, -3, 3, -4, 4, -5
.
我对pythonicness的评估:
这个单线程的好处是它很短并且显示出对称性(+i//2
和-i//2
).
但不好的是,这种对称性具有欺骗性:
人们可能会认为这与翻转的标志-i//2
相同i//2
.但是在Python中,整数除法返回结果的底限而不是截断为零.所以-1//2 == -1
.
此外,我发现通过索引访问列表元素比pythonic更少迭代.
Tad*_*sen 27
cycle
从前锋iter
和reversed
一个获得物品之间.只要确保你停下len(a)
来islice
.
from itertools import islice, cycle
iters = cycle((iter(a), reversed(a)))
b = [next(it) for it in islice(iters, len(a))]
>>> b
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
Run Code Online (Sandbox Code Playgroud)
这很容易放在一行,但后来变得更难阅读:
[next(it) for it in islice(cycle((iter(a),reversed(a))),len(a))]
Run Code Online (Sandbox Code Playgroud)
将它放在一行也会阻止你使用另一半迭代器,如果你想:
>>> iters = cycle((iter(a), reversed(a)))
>>> [next(it) for it in islice(iters, len(a))]
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
>>> [next(it) for it in islice(iters, len(a))]
[5, 4, 6, 3, 7, 2, 8, 1, 9, 0]
Run Code Online (Sandbox Code Playgroud)
Python 2.7中非常好的单行程序:
results = list(sum(zip(a, reversed(a))[:len(a)/2], ()))
>>>> [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
Run Code Online (Sandbox Code Playgroud)
首先用反向压缩列表,取一半列表,将元组加起来形成一个元组,然后转换为列表.
在Python 3,zip
返回一个生成器,所以你必须使用islice
从itertools
:
from itertools import islice
results = list(sum(islice(zip(a, reversed(a)),0,int(len(a)/2)),()))
Run Code Online (Sandbox Code Playgroud)
编辑:看来这只是完全适用于偶数列表的长度-奇列表的长度将省略中间元素:(一小修正int(len(a)/2)
至int(len(a)/2) + 1
会给你一个重复的中间值,因此被警告.
你可以pop
来回:
b = [a.pop(-1 if i%2 else 0) for i in range(len(a))]
Run Code Online (Sandbox Code Playgroud)
注意:这会破坏原始列表a
.
使用正确的工具.
from toolz import interleave, take
b = list(take(len(a), interleave((a, reversed(a)))))
Run Code Online (Sandbox Code Playgroud)
首先,我尝试了类似于Raymond Hettinger的迭代工具(Python 3)的解决方案.
from itertools import chain, islice
interleaved = chain.from_iterable(zip(a, reversed(a)))
b = list(islice(interleaved, len(a)))
Run Code Online (Sandbox Code Playgroud)
与其他一些答案没有太大的不同,但它避免了用于确定索引符号的条件表达式.
a = range(10)
b = [a[i // (2*(-1)**(i&1))] for i in a]
Run Code Online (Sandbox Code Playgroud)
i & 1
在0和1之间交替.这使得指数在1和-1之间交替.这会导致索引除数在2和-2之间交替,这会导致索引从一端到另一端交替i
增加.序列是a[0]
,a[-1]
,a[1]
,a[-2]
,a[2]
,a[-3]
,等.
(我迭代i
,a
因为在这种情况下,每个值a
都等于它的索引.通常,迭代range(len(a))
.)
你问题背后的基本原理是所谓的roundrobin算法.该itertools
-documentation页包含了可能实现的吧:
from itertools import cycle, islice
def roundrobin(*iterables):
"""This function is taken from the python documentation!
roundrobin('ABC', 'D', 'EF') --> A D E B F C
Recipe credited to George Sakkis"""
pending = len(iterables)
nexts = cycle(iter(it).__next__ for it in iterables) # next instead of __next__ for py2
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
Run Code Online (Sandbox Code Playgroud)
所以你要做的就是将你的列表分成两个子列表,一个从左端开始,一个从右端开始:
import math
mid = math.ceil(len(a)/2) # Just so that the next line doesn't need to calculate it twice
list(roundrobin(a[:mid], a[:mid-1:-1]))
# Gives you the desired result: [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
Run Code Online (Sandbox Code Playgroud)
或者,您可以创建一个更长的列表(包含从左到右的序列中的交替项目以及从右到左的完整序列的项目),并且只采用相关元素:
list(roundrobin(a, reversed(a)))[:len(a)]
Run Code Online (Sandbox Code Playgroud)
或者将其用作显式生成器next
:
rr = roundrobin(a, reversed(a))
[next(rr) for _ in range(len(a))]
Run Code Online (Sandbox Code Playgroud)
或@Tadhg McDonald-Jensen建议的快速变体(谢谢!):
list(islice(roundrobin(a,reversed(a)),len(a)))
Run Code Online (Sandbox Code Playgroud)