【问题标题】:Python reverse-stride slicingPython反向跨步切片
【发布时间】:2011-08-13 11:28:21
【问题描述】:

我的问题的一个具体例子是,“在这个例子中我怎样才能得到'3210'?”


>>> foo = '0123456'
>>> foo[0:4]
'0123'
>>> foo[::-1]
'6543210'
>>> foo[4:0:-1] # I was shooting for '3210' but made a fencepost error, that's fine, but...
'4321'
>>> foo[3:-1:-1] # How can I get '3210'?
''
>>> foo[3:0:-1]
'321'

我可以写 foo[4:0:-1]、foo[5:1:-1] 等并得到我所期望的,这似乎很奇怪,但是没有办法写出切片所以我得到'3210'。

执行此操作的临时方法是 foo[0:4][::-1],但这会在此过程中创建两个字符串对象。我将执行这个操作数十亿次,所以每个字符串操作都很昂贵。

我一定错过了一些愚蠢而简单的事情。感谢您的帮助!

【问题讨论】:

    标签: python string slice


    【解决方案1】:

    简单地排除结束范围索引...

    >>> foo[3::-1]
    '3210'
    

    具有讽刺意味的是,关于我认为你没有尝试过的唯一选择。

    【讨论】:

    • 问题是这个切片操作被用在一个算法中,它有点像下面这样工作:foo[i:i-4:-1],并以高 'i' 和走下来。我不能只删除'i'。我想我可以使用边缘情况并说“如果 i-4
    • 如果这是唯一的方法,我会将其设置为“接受”,但使用删除中间操作数的边缘情况作为最终的“反向子字符串”(而不是任何其他反向substrings) 看起来很笨重。
    • 好吧,公平地说,你没有在你的问题中包含那个条件,即使那样你也可以将那个“笨拙”的逻辑包装在一个函数中并完成它。
    • @Alice -1 始终是列表的最后一个元素(或它与前一个元素之间的空间,取决于您对列表索引的看法)。我同意,当使用负步骤时,如果 -1 表示超出列表开头的空间会很好,就像 len(L) 表示超出列表末尾的空间一样。如果你使用 -1 作为停止和步骤 -1,你将有一个空切片。
    • 如果要使用负数索引,则必须计算负数步数,例如 s[len(s)-1 : - (len(s)+1) : -1]
    【解决方案2】:

    如果您正在寻找比扩展切片符号更易读的东西:

    >>> foo = '0123456'
    >>> ''.join(reversed(foo[0:4]))
    '3210'
    

    【讨论】:

    • 谢谢,但是不,这会创建 三个 内存对象。 join 和 reversed 非常快,但我认为可以肯定地假设扩展切片表示法会更快。
    • 我通常假设如果您使用 python 编写,效率并不是最重要的事情。唯一可能重要的另一件事是可读性,因此我对此进行了优化。扩展切片表示法,尤其是负步长值,会让人觉得非常不透明。
    【解决方案3】:

    在切片符号中省略结束索引:

    >>> foo = '0123456'
    >>> foo[3::-1]
    '3210'
    

    如果您必须多次这样做,请创建一个可以反复使用的切片对象

    >>> i = slice(3,None,-1)
    >>> foo[i]
    '3210'
    

    【讨论】:

    • 哇!我不知道 slice() 。 +1
    • 你也可以去 foo[3:None if i
    【解决方案4】:

    除了上述解决方案之外,您还可以执行以下操作:

    foo = '0123456'
    foo[-4::-1]
    

    我猜如果 foo 会改变长度,这可能不是最好的解决方案,但如果长度是静态的,它会起作用。

    【讨论】:

      【解决方案5】:

      您可以使用s[::-1] 来反转整个字符串。但是如果你想反转每个固定长度的子串,你可以先提取子串,然后反转整个子串。例如,假设我们需要检查字符串foo的每个长度为3的子字符串是否是回文,我们可以这样做:

      >>> foo = '0102030'
      >>> for i in range(len(foo)-3):
      ...     if foo[i:i+3] == foo[i:i+3][::-1]:
      ...         print(foo[i:i+3], 'is a palindrome')
      ...     else:
      ...         print(foo[i:i+3], 'is not a palindrome')
      ...
      010 is a palindrome
      102 is not a palindrome
      020 is a palindrome
      203 is not a palindrome
      030 is a palindrome
      

      如果你想检查一个子字符串是否是这样的回文:

      if foo[i:i+3] == foo[i+2:i-1:-1]:
          ...
      

      您将无法处理i0 的情况,因为您实际上是将foo[0:3]foo[2:-1:-1] 进行比较,这相当于foo[2:n-1:-1],而foo[2:n-1:-1] 又是一个空字符串。

      第一个解决方案的唯一缺点是它使用了更多的内存,但这没什么大不了的。

      【讨论】:

        【解决方案6】:

        看完“技术文档”(here)——具体是那句话:

        如果任一边界为负,则将序列的长度添加到其中。

        我决定试试这个,它奏效了:

        >>> foo = '0123456'
        >>> foo[3:-1-len(foo):-1]
        '3210'
        >>>
        

        所以我认为以编程方式确定“终点”的最佳答案是提供一个命名良好的辅助函数,清楚地表明它的参数总是被视为正偏移量,也许是special_slice()

        我认为这种“特殊”情况的明确性非常重要,因为许多常见和重要的用例取决于负偏移的默认行为(即向它们添加长度)。就我个人而言,我经常使用“-1”端点来表示:在最后一个元素之前停止。

        所以,根据您的评论:

        ... 算法的工作原理有点像如下:foo[i:i-4:-1],以高 'i' 开始并向下走。

        我可能会做以下事情:

        def slice_by_len(data, start, length, step=1):
            end = start + length if step > 0 else start - length
            if end < 0:
                # Fix the negative offset to get what we really want
                end -= len(data)
            return data[start:end:step]
        

        然后为所需的每个切片调用它:

        foo_part = slice_by_len(foo, i, 4, -1)
        

        上面的内容很容易在 'i' 的值上循环

        【讨论】:

        • 尝试在 Python 中实现这一点很难看!我参考了你的回答here
        【解决方案7】:
        s="this is my world"
        pattern=re.findall(r'\S+',s)
        a=[]
        for i in range(len(pattern)):
            a.append((pattern[i][::-1]))
        print (a)
        print (" ".join(a))
        

        【讨论】:

          【解决方案8】:

          给定:

          >>> foo = '0123456'
          

          所需的字符串 3210 是从第 3 个索引到第 0 个字符:

          >>> stop_idx=0
          >>> start_idx=3
          

          这里有两个通用的解决方案:

          1. 取正向切片然后反转它:

            >>> foo[stop_idx:start_idx+1][::-1]
            '3210'
            
          2. 基于this answer,使用负步长,在第一个元素之前停止1个元素(加上停止偏移):

            >>> foo[start_idx:stop_idx-len(foo)-1:-1]
            '3210'
            
            >>> a[start_idx:stop_idx-len(a)-1:-1]
            [2, 1]
            

          比较执行时间,第一个版本更快:

          >>> timeit.timeit('foo[stop_idx:start_idx+1][::-1]', setup='foo="012345"; stop_idx=0; start_idx=3', number=10_000_000)
          1.7157553750148509
          >>> timeit.timeit('foo[start_idx:stop_idx-len(foo)-1:-1]', setup='foo="012345"; stop_idx=0; start_idx=3', number=10_000_000)
          1.9317215870250948
          

          【讨论】:

            猜你喜欢
            • 2020-09-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-01-25
            • 1970-01-01
            相关资源
            最近更新 更多