次のように、文字列内の「特殊」文字が最初に出現するインデックスを見つけたいと思います。
>>> "Hello world!".index([' ', '!'])
5
…ただし、これは有効な Python 構文ではありません。もちろん、この動作をエミュレートする関数を書くこともできます。
def first_index(s, characters):
i = []
for c in characters:
try:
i.append(s.index(c))
except ValueError:
pass
if not i:
raise ValueError
return min(i)
正規表現を使用することもできますが、どちらの解決策も少しやり過ぎのようです。Python でこれを行う「まともな」方法はあるでしょうか?
ベストアンサー1
使用できます列挙するそして次とともにジェネレータ式最初に一致したものを取得するか、s に文字がない場合は None を返します。
s = "Hello world!"
st = {"!"," "}
ind = next((i for i, ch in enumerate(s) if ch in st),None)
print(ind)
一致するものがない場合、デフォルトの戻り値として任意の値を次に渡すことができます。
関数を使用して ValueError を発生させる場合:
def first_index(s, characters):
st = set(characters)
ind = next((i for i, ch in enumerate(s) if ch in st), None)
if ind is not None:
return ind
raise ValueError
入力が小さい場合、セットを使用しても大きな違いはありませんが、大きな文字列の場合はより効率的になります。
いくつかのタイミング:
文字列内の文字セットの最後の文字:
In [40]: s = "Hello world!" * 100
In [41]: string = s
In [42]: %%timeit
st = {"x","y","!"}
next((i for i, ch in enumerate(s) if ch in st), None)
....:
1000000 loops, best of 3: 1.71 µs per loop
In [43]: %%timeit
specials = ['x', 'y', '!']
min(map(lambda x: (string.index(x) if (x in string) else len(string)), specials))
....:
100000 loops, best of 3: 2.64 µs per loop
文字列に含まれない、より大きな文字セット:
In [44]: %%timeit
st = {"u","v","w","x","y","z"}
next((i for i, ch in enumerate(s) if ch in st), None)
....:
1000000 loops, best of 3: 1.49 µs per loop
In [45]: %%timeit
specials = ["u","v","w","x","y","z"]
min(map(lambda x: (string.index(x) if (x in string) else len(string)), specials))
....:
100000 loops, best of 3: 5.48 µs per loop
文字列内の文字セットの最初の文字:
In [47]: %%timeit
specials = ['H', 'y', '!']
min(map(lambda x: (string.index(x) if (x in string) else len(string)), specials))
....:
100000 loops, best of 3: 2.02 µs per loop
In [48]: %%timeit
st = {"H","y","!"}
next((i for i, ch in enumerate(s) if ch in st), None)
....:
1000000 loops, best of 3: 903 ns per loop