【问题标题】:How to slice a circular list如何对圆形列表进行切片
【发布时间】:2022-01-17 07:05:51
【问题描述】:

假设我有如下列表:

lst = [0,10,20,30,40,50,60,70]

我想要以循环顺序从 index = 5index = 2 的 lst 元素。

lst[5:2] 产生[] 我想要lst[5:2] = [50,60,70,0,10]。有没有简单的库函数可以做到这一点?

【问题讨论】:

标签: python list itertools cycle


【解决方案1】:

如果第二项小于第一项,只需将切片一分为二:

lst = [0,10,20,30,40,50,60,70]


def circslice(l, a, b):
    if b>=a:
        return l[a:b]
    else:
        return l[a:]+l[:b]
    
circslice(lst, 5, 2)

输出:[50, 60, 70, 0, 10]

【讨论】:

    【解决方案2】:

    按照 cmets 中的建议使用 deque

    from collections import deque
    
    d = deque(lst)
    
    a,b = 5,2
    d.rotate(-a)
    list(d)[:len(lst)-a+b]
    

    注意。我发现它不是很实用,因为它需要复制列表来创建双端队列,并复制另一个副本来切片

    【讨论】:

    • 这与我想出的答案相同,但没有发布,因为它有点奇怪
    • 好的,我明白为什么了;)
    【解决方案3】:

    对于允许您仍然使用本机切片语法并保持静态类型兼容性的东西,您可以在序列周围使用轻量级包装器类:

    from typing import Generic, Protocol, TypeVar
    
    
    S = TypeVar('S', bound="ConcatSequence")
    
    
    class CircularView(Generic[S]):
    
        def __init__(self, seq: S) -> None:
            self.seq = seq
    
        def __getitem__(self, s: slice) -> S:
            if s.start <= s.stop:
                return self.seq[s]
            else:
                wrap = len(self.seq) % s.step if s.step else 0
                return self.seq[s.start::s.step] + self.seq[wrap:s.stop:s.step]
    
    
    lst = [0, 10, 20, 30, 40, 50, 60, 70]
    
    print(CircularView(lst)[2:5])    # [20, 30, 40]
    print(CircularView(lst)[5:2])    # [50, 60, 70, 0, 10]
    print(CircularView(lst)[5:2:2])  # [50, 70, 0]
    print(CircularView(lst)[5:3:2])  # [50, 70, 0, 20]
    print(CircularView(lst)[4:3:3])  # [40, 70, 20]
    
    

    带有可选的protocol 用于静态类型

    class ConcatSequence(Protocol):
        """
        A sequence that implements concatenation via '__add__'.
    
        This protocol is required instead of using 
        'collections.abc.Sequence' since not all sequence types
        implement '__add__' (for example, 'range').
        """
    
        def __add__(self, other):
            ...
    
        def __getitem__(self, item):
            ...
    
        def __len__(self):
            ...
    

    这个方法passes type checking with mypy

    【讨论】:

      【解决方案4】:

      你可以使用这样的函数:

      def circular_indexing(list_, start_index, end_index) -> list:
          return [*list_[start_index:len(list_)], *list_[0:end_index]]
      

      例如:

      list1 = [0, 1, 2, 3]
      
      def circular_indexing(list_, start_index, end_index) -> list:
          return [*list_[start_index:len(list_)], *list_[0:end_index]]
      
      print(circular_indexing(list1, 2, 1))
      

      输出:[2, 3, 0]

      【讨论】:

        【解决方案5】:

        这个问题有两种快速/简单的解决方案。

        第一个也是更复杂的方法是覆盖 python list.__getitem__ 方法的默认 python 库实现,which has been referenced in other places on StackOverflow

        这将允许您像往常一样引用切片,即list[5:3],理论上它会按照您定义的方式运行。这将是默认库的“本地扩展”。

        横向地,您可以实现自己的函数,该函数将以循环方式遍历您的列表,满足您自己的标准。 一些伪代码:

        def foo(left_idx, right_idx):
            if right_idx < left_idx:
                wrap index when right bound has been reached
            else:
                iterate normally
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-07-08
          • 2020-08-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-27
          相关资源
          最近更新 更多