在Python中根据索引及其后的项目切片列表

时间:2019-05-14 17:42:53

标签: python list

我试图在pygame中创建一个平滑的旋转系统,所以我设置了一个度值数组,如下所示:

DEGREES = [
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

我会选择一个角度,然后将这个假想的圆一分为二,以便更轻松地找到到达目标方向的最短路径。

我要说的是,如何选择一个特定的值,例如90,然后才能找到其后的前12个元素,包括末尾环绕的索引?

因此,采用较早的值并将其应用于该列表,我将得到以下内容:

[90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

使用切片符号,我尝试这样做:

index = DEGREES.index(90)
print(DEGREES[index-12:index]) # start 12 values back, stop at index

但这只会打印一个空数组。

有没有一种方法可以对列表进行切片,以便可以获取所使用索引后面的12个先前值?

16 个答案:

答案 0 :(得分:34)

带角度的算术

您的目标不是切片,连接或反向列表。您的目标是对度进行基本算术运算,并将结果保持在0359之间。为此,您确实应该使用modulo运算符%

>>> 90 % 360
90
>>> 390 % 360
30
>>> -60 % 360
300
>>> 360 % 360
0

回到问题

如果您只想将此切片用于恒定增量的度数,则可以直接生成所需的列表:

>>> STEP = 15
>>> list(range(0, 360, STEP))
[0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345]
>>> def previous_degrees(start, n, step=STEP):
...     return [(start - i * step) % 360 for i in range(n + 1)]
... 
>>> previous_degrees(90, 12)
[90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]
>>> previous_degrees(90, 12, 30)
[90, 60, 30, 0, 330, 300, 270, 240, 210, 180, 150, 120, 90]
>>> previous_degrees(90, 6, 45)
[90, 45, 0, 315, 270, 225, 180]

您的真实问题

您在评论中写道:

  

此度数数组旨在平滑旋转   我试图在pygame中创建的系统。通常我会发现   当前方向与目标方向之间的差异   并从那里递增,但由于旋转以零I翻转   必须对值进行硬编码以确保它始终可以   最短的路线。

您需要从两个角度确定是顺时针还是逆时针旋转。您可以再次使用模数以确保旋转范围在-180°和179°之间:

def shortest_rotation(start_angle, end_angle):
    return (end_angle - start_angle + 180) % 360 - 180

这是一个例子:

>>> shortest_rotation(0, 90)
90
>>> shortest_rotation(90, 0)
-90
>>> shortest_rotation(90, 90)
0
>>> shortest_rotation(90, 330)
-120
>>> shortest_rotation(0, 180)
-180
>>> shortest_rotation(0, 181)
-179
>>> shortest_rotation(0, 179)
179
>>> shortest_rotation(10, 350)
-20

您现在可以创建一个角度列表,以最短的方向转动:

def rotation_steps(start_angle, end_angle, n):
    increment = shortest_rotation(start_angle, end_angle) / n
    return [(start_angle + i * increment) % 360 for i in range(n + 1)]

例如:

>>> rotation_steps(90, 270, 12)
[90.0, 75.0, 60.0, 45.0, 30.0, 15.0, 0.0, 345.0, 330.0, 315.0, 300.0, 285.0, 270.0]
>>> rotation_steps(10, 350, 2)
[10.0, 0.0, 350.0]

如果end_angle不是整数,则列表使用浮点数来避免丢失increment

答案 1 :(得分:16)

或者您可以使用deque

from collections import deque
from itertools import islice

dq = deque(reversed((0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345)))

index = dq.index(90)
dq.rotate(-index)
res = list(islice(dq, 13))
# [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

您可以将其用作功能:

def f(i):
    dq.rotate(-dq.index(i))
    return list(islice(dq, 13))

#  f(90) = [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

答案 2 :(得分:10)

类似的事情可能更直接:

index = DEGREES.index(90)
print([DEGREES[i] for i in range(index, index-13, -1)])

答案 3 :(得分:6)

在这些情况下,方便使用的NumPy函数是np.roll,正如其名称所指定的那样,它可以滚动数组中的元素,也如文档中所述:

  

超出最后位置的元素会在第一个位置重新引入

正是我们所需要的,以便将列表中的第一项倒退到出现90的索引处。

所以一种方法可能是使用90列表方法使用index出现在其中的索引,并将数组向上移动到-k个位置,k是给定的指数。然后,我们就可以对列表进行切片,并将其最后的n元素取反:

import numpy as np

l = [
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

def roll_n_reversed(l, i, n):
    return np.roll(l, -l.index(i)-1)[:-(n+1):-1]

roll_n_reversed(l, 90, 13)

哪个会产生预期的输出:

array([ 90,  75,  60,  45,  30,  15,   0, 345, 330, 315, 300, 285, 270])

答案 4 :(得分:6)

基于itertoolscycleislice)的解决方案:

from itertools import cycle, islice

DEGREES = cycle(reversed((
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345)))

next(item for item in DEGREES if item == 90)  # advance to next 90
res = [90] + list(islice(DEGREES, 12))
# [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

您可以将其打包为单行功能:

def f(i):
    return [next(d for d in DEGREES if d == i), *islice(DEGREES, 12)]

#  f(90) = [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

或者甚至使用dropwhile(如注释中所述):

from itertools import cycle, islice, dropwhile

def f(i):
    return list(islice(dropwhile(lambda d: d != i, DEGREES), 13))

如果您的列表与上面打印的完全一样,您还可以使用range快速生成切片:

def f(i, d=15, n=13):
    return [deg % 360 for deg in range(i, i-n*d, -d)]

# f(90) = [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

答案 5 :(得分:3)

您可以使用此:

previous12 = [DEGREES[p-i] for p in [DEGREES.index(90)] for i in range(13)]

或者这个:

previous12 = (DEGREES+DEGREES[:DEGREES.index(90)+1])[:-14:-1]

答案 6 :(得分:2)

不幸的是,您无法一刀切。您可以将片段连接起来,这可能会有些尴尬:

DEGREES = [
    0, 15, 30, 45, 60, 
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

index = DEGREES.index(90)
result = DEGREES[index:index - 12:-1] if index >= 12 else (DEGREES[index::-1] + DEGREES[:index - 12:-1])
print(result)
# [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285]

或者只使用列表理解:

DEGREES = [
    0, 15, 30, 45, 60, 
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

index = DEGREES.index(90)
result = [DEGREES[i] for i in range(index, index - 12, -1)]
print(result)
# [90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285]

答案 7 :(得分:0)

我认为itertools.chain在这里可能有用:

from itertools import chain

DEGREES = [
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345
]

def get_list_of_degrees(degree, resulting_list_length):
    index = DEGREES.index(degree)
    lower_index = index - (resulting_list_length)
    if index >= resulting_list_length:
        result = DEGREES[lower_index: index]  # start 12 values back, stop at index
    else:
        result = list(chain(DEGREES[lower_index:], DEGREES[:index])) # start 12 values back, stop at index
    return result

my_degrees = get_list_of_degrees(90, 12)
print(my_degrees)

收益:

  

[270,285,300,315,330,345,0,15,30,45,60,75]

您指定的是哪个,只是向后

也许更直接,更可缩放/可更改的方法是在没有DEGREES列表的情况下即时生成角度。像这样:

def get_angles(start_angle=90, increment=-15, return_array_size=12):
    angles = [i for i in range(start_angle + increment, start_angle + (return_array_size*increment) + increment, increment)]
    for index in range(len(angles)):
        while angles[index] < 0:
            angles[index] += 360
    return angles

print(get_angles())

返回:

  

[75,60,45,30,15,0,345,330,315,300,285,270]

尽管您可以灵活地轻松地仅返回5个角度,或者步距为2度,等等。例如

print(get_angles(increment=-2))

现在返回:

  

[88,86,84,82,80,78,76,74,72,70,68,66]

只需对代码做很少的改动(否则,您将不得不生成一个新的DEGREES数组来完成此操作)

答案 8 :(得分:0)

列表为空的原因是,您根本没有12个值在90之前的项目。

您需要的是处理此异常的语句:

index = DEGREES.index(90)
if index >= 12:
    print(DEGREES[index-12:index])
else: 
    print(DEGREES[:index])

答案 9 :(得分:0)

按列表切片:

DEGREES = [
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

value = 90
index = DEGREES.index(value)



result = DEGREES[:index+1][::-1] + DEGREES[index+1:][::-1]
result = result[:13]
print(result)

输出

[90, 75, 60, 45, 30, 15, 0, 345, 330,
 315, 300, 285, 270]

RES= [ DEGREES[i] for i in range(index,index-12-1,-1)]

答案 10 :(得分:0)

我认为您需要做一些算术运算。

index = DEGREES.index(90) + 1
offset = 12
start = index - offset
length = len(DEGREES)
print(
    list(reversed(DEGREES[max(0, start):index])) + 
    (list(reversed(DEGREES[length + start - 1 :length])))
     if start < 0
     else [])
)

或者:

答案 11 :(得分:0)

我建议您尝试itertools.cycle()以获得任意数量的先前值。

只需反转列表,然后尝试cycle()

import itertools

degrees = [0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345]
n=12
degrees.reverse()
ind = degrees.index(90)
degrees = degrees[ind:]+degrees[:ind]
rev_cycle = itertools.cycle(degrees)
for i in range(n+1):
    print(next(rev_cycle))

这很有效,因为它使用了生成器。

答案 12 :(得分:0)

在您的示例中,您希望打印的元素是DEGREES[-6:6]。您可能想要添加条件,以照顾开始循环返回的起始索引。像这样:

DEGREES = [
    0, 15, 30, 45, 60, 
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

index = DEGREES.index(90)
start_idx = index - 12
if start_idx < 0:
    print(DEGREES[start_idx:] + DEGREES[:index + 1])
else:
    print(DEGREES[start_idx:index + 1])

这应该返回以下内容:

[270, 285, 300, 315, 330, 345, 0, 15, 30, 45, 60, 75, 90]

这是您的解决方案,但相反。

答案 13 :(得分:0)

DEGREES = [
    0, 15, 30, 45, 60, 
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]


index = DEGREES.index(90)

subFront = DEGREES[:index + 1][-12:]
subFront.reverse()

remainLen = 12 - len(subFront) + 1
if remainLen > 0:
    subBack = DEGREES[-remainLen:]
    subBack.reverse()
    subFront = subFront + subBack
print(subFront)
[90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]

答案 14 :(得分:0)

import numpy as np

DEGREES = [
    0, 15, 30, 45, 60,
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]
idx = DEGREES.index(90)
new_list = DEGREES[::-1]
newList = np.roll(new_list, idx+1)
print(newList)

答案 15 :(得分:0)

我有this handy function实现了包裹切片。通过直接计算角度值可以更好地解决您的用例,如其他答案所示。这可能会解决问题:

def wrapping_slice(lst, *args):
    return [lst[i%len(lst)] for i in range(*args)]

示例:

DEGREES = [
    0, 15, 30, 45, 60, 
    75, 90, 105, 120,
    135, 150, 165, 180,
    195, 210, 225, 240,
    255, 270, 285, 300,
    315, 330, 345,
]

start = DEGREES.index(90)

print(wrapping_slice(DEGREES, start, start-13, -1))

输出为:

$ python test.py
[90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]