我希望将列表作为输入,然后创建另一个列表,其中包含原始列表中相邻元素的元组(或子列表),包含起始元素和结束元素。输入/输出如下所示:
l_in = [0, 1, 2, 3]
l_out = [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]
我的问题与另一个名为getting successive adjacent elements of a list的问题密切相关,但另一个问题没有考虑到结束元素的包装,只处理元素对而不是三元组。
我有一个更长的方法来做这个涉及旋转deques和拉链他们:
from collections import deque
l_in = [0, 1, 2, 3]
deq = deque(l_in)
deq.rotate(1)
deq_prev = deque(deq)
deq.rotate(-2)
deq_next = deque(deq)
deq.rotate(1)
l_out = list(zip(deq_prev, deq, deq_next))
# l_out is [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]
但是,我觉得使用其他内置Python功能可能有更优雅(和/或更有效)的方法。例如,如果rotate()
的{{1}}函数返回旋转列表而不是将其修改到位,则可能是一行或两行(尽管这种方法可能会将旋转列表压缩在一起)不是最有效的)。如何更优雅和/或更有效地完成这项工作?
答案 0 :(得分:1)
这可以通过切片完成:
l_in = [0, 1, 2, 3]
l_in = [l_in[-1]] + l_in + [l_in[0]]
l_out = [l_in[i:i+3] for i in range(len(l_in)-2)]
好吧,还是这样的变态:
div = len(l_in)
n = 3
l_out = [l_in[i % div: i % div + 3]
if len(l_in[i % div: i % div + 3]) == 3
else l_in[i % div: i % div + 3] + l_in[:3 - len(l_in[i % div: i % div + 3])]
for i in range(3, len(l_in) + 3 * n + 2)]
您可以指定迭代次数。
答案 1 :(得分:1)
一种方法可能是将itertools
与more_itertools.windowed
结合使用:
import itertools as it
import more_itertools as mit
l_in = [0, 1, 2, 3]
n = len(l_in)
list(it.islice(mit.windowed(it.cycle(l_in), 3), n-1, 2*n-1))
# [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]
这里我们生成了sliding windows的无限循环并切割了所需的子集。
FWIW,这里是后一代码的抽象,给出了任何可迭代输入的一般灵活解决方案,例如: range(5)
,"abcde"
,iter([0, 1, 2, 3])
等等:
def get_windows(iterable, size=3, offset=-1):
"""Return an iterable of windows including an optional offset."""
it1, it2 = it.tee(iterable)
n = mit.ilen(it1)
return it.islice(mit.windowed(it.cycle(it2), size), n+offset, 2*n+offset)
list(get_windows(l_in))
# [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]
list(get_windows("abc", size=2))
# [('c', 'a'), ('a', 'b'), ('b', 'c')]
list(get_windows(range(5), size=2, offset=-2))
# [(3, 4), (4, 0), (0, 1), (1, 2), (2, 3)]
注意:more-itertools
是一个单独的库,可以通过以下方式轻松安装:
> pip install more_itertools
答案 2 :(得分:0)
当我写这个问题时,我想出了一个更好的解决方案,但我已经完成了编写它的工作,所以这里有。这个解决方案至少要简洁得多:
deque
有关如何在Python中旋转列表的不同答案,请参阅this post。
上面的单行解决方案应该至少与问题中的解决方案一样有效(基于我的理解),因为切片不应该比旋转和复制deques更昂贵(见https://wiki.python.org/moin/TimeComplexity)。
尽管如此,仍然欢迎提供更高效(或优雅)解决方案的其他答案。
答案 3 :(得分:0)
你发现有一个基于习语lst[i:] + lst[:i]
在理解中使用它为变量n
获取所需的相邻元素的数量更为通用[lst[i:] + lst[:i] for i in range(n)]
所以一切都可以参数化,循环旋转中的相邻元素n
的数量和'阶段'p
,如果不是'自然'0基本索引的起点,尽管默认p=-1
设置为-1以适合所需的输出
tst = list(range(4))
def rot(lst, n, p=-1):
return list(zip(*([lst[i+p:] + lst[:i+p] for i in range(n)])))
rot(tst, 3)
Out[2]: [(3, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 0)]
根据评论
显示短代码