私の質問の具体的な例は、「この例で「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つの文字列オブジェクトが作成されます。私はこの操作を文字通り数十億回実行するので、すべての文字列操作は高価です。
私はばかげて簡単なものを見逃しているに違いない。ご協力いただきありがとうございます!
単に範囲インデックスを除外します...
>>> foo[3::-1]
'3210'
皮肉なことに、私があなたが試みなかったと思う唯一のオプションについて。
拡張スライス表記よりも人間が読めるものを探している場合:
>>> foo = '0123456'
>>> ''.join(reversed(foo[0:4]))
'3210'
スライス表記の終了インデックスを省略します。
>>> foo = '0123456'
>>> foo[3::-1]
'3210'
これを何度も行う必要がある場合は、何度も使用できるスライスオブジェクトを作成します。
>>> i = slice(3,None,-1)
>>> foo[i]
'3210'
「技術文書」( ここ )を読んだ後-特に文章:
いずれかの境界が負の場合、シーケンスの長さがそれに追加されます。
私はこれを試すことにしました、そしてそれはうまくいきました:
_>>> 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'の値をループする可能性があります
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]:
...
実際には0
とfoo[0:3]
を比較しているため、i
がfoo[2:-1:-1]
である場合を処理できません。これは、foo[2:n-1:-1]
と同等です。これは空の文字列です。
最初のソリューションの唯一の欠点は、少し多くのメモリを使用することですが、大したことではありません。
上記のソリューションに加えて、次のようなことができます。
foo = '0123456'
foo[-4::-1]
Fooが長さを変更するのであれば、これは最善の解決策ではないかもしれませんが、長さが静的であれば機能します。
与えられた:
>>> foo = '0123456'
目的の文字列3210
は3番目のインデックスから0番目の文字までです。
>>> stop_idx=0
>>> start_idx=3
2つの一般的なソリューションを次に示します。
前方スライスを取り、それを逆にします。
>>> foo[stop_idx:start_idx+1][::-1]
'3210'
この答え に基づいて、負のステップを使用し、最初の要素の前に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