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

Ale*_*XYX 39 python list

说我有一个度值数组,像这样:

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,
]
Run Code Online (Sandbox Code Playgroud)

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

这样说,我如何选择一个特定的值(例如)90,然后能够找到该值后面的前12个元素,包括结束时环绕的索引?

因此,采用该更早的值并将其应用于该列表,我将得到如下信息:

[90, 75, 60, 45, 30, 15, 0, 345, 330, 315, 300, 285, 270]
Run Code Online (Sandbox Code Playgroud)

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

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

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

有没有办法对列表进行切片,这样我就可以得到所使用索引后面的12个先前值?

编辑:

原来这是一个XY问题,我不好。最初,我试图在Pygame中创建一个平滑的旋转系统,但尝试计算角度不起作用,我问了这个问题以解决另一个我尝试实现的想法的问题。我最终接受了有助于建立平滑旋转系统的答案,但是下面的原始问题有相关答案。

Eri*_*nil 39

角度算术

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

>>> 90 % 360
90
>>> 390 % 360
30
>>> -60 % 360
300
>>> 360 % 360
0
Run Code Online (Sandbox Code Playgroud)

回到问题

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

>>> 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]
Run Code Online (Sandbox Code Playgroud)

你真正的问题

您在评论中写道:

这个度数数组旨在与我试图在pygame中创建的平滑旋转系统一起使用。通常,我只是会发现当前方向和目标方向之间的差异,然后从那里开始递增,但是由于旋转翻转为零,因此我必须对这些值进行硬编码,以确保始终能走到最短的路径。

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

def shortest_rotation(start_angle, end_angle):
    return (end_angle - start_angle + 180) % 360 - 180
Run Code Online (Sandbox Code Playgroud)

这是一个例子:

>>> 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
Run Code Online (Sandbox Code Playgroud)

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

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)]
Run Code Online (Sandbox Code Playgroud)

举个例子:

>>> 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]
Run Code Online (Sandbox Code Playgroud)

该列表使用float以避免丢失end_angleif increment不是整数。

  • 这比我看到的其他解决方案要优雅得多,我今天将尝试一下,然后再试一次,不管是否有效。我确实很抱歉,我并没有完全说明为什么要使用此系统,如果我想尝试超出问题的基本前提进行解释,我会感到沮丧/无视。 (4认同)
  • @Pygasm不用担心,很难编写一个好问题,也很难包含足够的信息,但又不要写太长的问题。请参阅[* XY问题*](https://meta.stackexchange.com/a/66378/390071)。 (2认同)
  • 像魅力一样工作。非常感谢,下次我将尽量避免出现XY问题。 (2认同)

hir*_*ist 18

或者您可以使用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]
Run Code Online (Sandbox Code Playgroud)

您可以将其用作函数:

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]
Run Code Online (Sandbox Code Playgroud)


Joh*_*ado 11

这样的事情可能更直接:

index = DEGREES.index(90)
print([DEGREES[i] for i in range(index, index-13, -1)])
Run Code Online (Sandbox Code Playgroud)


yat*_*atu 7

在这些情况下,派上用场的NumPy函数是np.roll,顾名思义,该函数滚动数组中的元素,也如文档中所述:

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

这正是我们需要的,以便将列表中的第一项从后面滚动到90出现的索引。

因此,一种方法可能是90使用通过indexlist方法出现的索引,然后将数组移至给定索引的-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)
Run Code Online (Sandbox Code Playgroud)

产生预期的输出:

array([ 90,  75,  60,  45,  30,  15,   0, 345, 330, 315, 300, 285, 270])
Run Code Online (Sandbox Code Playgroud)


hir*_*ist 7

基于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]
Run Code Online (Sandbox Code Playgroud)

您可以将其打包成一个单行函数:

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]
Run Code Online (Sandbox Code Playgroud)

甚至使用dropwhile(如评论中所述):

from itertools import cycle, islice, dropwhile

def f(i):
    return list(islice(dropwhile(lambda d: d != i, DEGREES), 13))
Run Code Online (Sandbox Code Playgroud)

如果您的列表与上面打印的完全一样,您还可以使用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]
Run Code Online (Sandbox Code Playgroud)