web-dev-qa-db-ja.com

Python逆ストライドスライス

私の質問の具体的な例は、「この例で「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]ですが、これによりプロセス内に2つの文字列オブジェクトが作成されます。私はこの操作を文字通り数十億回実行するので、すべての文字列操作は高価です。

私はばかげて簡単なものを見逃しているに違いない。ご協力いただきありがとうございます!

30
eblume

単に範囲インデックスを除外します...

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

皮肉なことに、私があなたが試みなかったと思う唯一のオプションについて。

37
Andrew White

拡張スライス表記よりも人間が読めるものを探している場合:

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

スライス表記の終了インデックスを省略します。

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

これを何度も行う必要がある場合は、何度も使用できるスライスオブジェクトを作成します。

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

「技術文書」( ここ )を読んだ後-特に文章:

いずれかの境界が負の場合、シーケンスの長さがそれに追加されます。

私はこれを試すことにしました、そしてそれはうまくいきました:

_>>> 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'の値をループする可能性があります

3
CrashNeb

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]:
    ...

実際には0foo[0:3]を比較しているため、ifoo[2:-1:-1]である場合を処理できません。これは、foo[2:n-1:-1]と同等です。これは空の文字列です。

最初のソリューションの唯一の欠点は、少し多くのメモリを使用することですが、大したことではありません。

3
Luan Gong

上記のソリューションに加えて、次のようなことができます。

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

Fooが長さを変更するのであれば、これは最善の解決策ではないかもしれませんが、長さが静的であれば機能します。

1
jack

与えられた:

>>> foo = '0123456'

目的の文字列3210は3番目のインデックスから0番目の文字までです。

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

2つの一般的なソリューションを次に示します。

  1. 前方スライスを取り、それを逆にします。

    >>> foo[stop_idx:start_idx+1][::-1]
    '3210'
    
  2. この答え に基づいて、負のステップを使用し、最初の要素の前に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
1
Tom Hale