web-dev-qa-db-ja.com

Python:渡された関数をtrueにするリストの最初の要素のインデックスを返します

list.index(x)関数は、値がxである最初のアイテムのリスト内のインデックスを返します。

パラメーターとして関数list_func_index()を持つindex()関数に似た関数f()があります。関数f()は、f(e)eを返すまで、リストのすべての要素Trueで実行されます。次に、list_func_index()eのインデックスを返します。

コードワイズ:

>>> def list_func_index(lst, func):
      for i in range(len(lst)):
        if func(lst[i]):
          return i
      raise ValueError('no element making func True')

>>> l = [8,10,4,5,7]
>>> def is_odd(x): return x % 2 != 0
>>> list_func_index(l,is_odd)
3

よりエレガントなソリューションはありますか? (および関数のより良い名前)

47
bandana

ジェネレーターを使用してワンライナーでそれを行うことができます:

next(i for i,v in enumerate(l) if is_odd(v))

ジェネレーターの良いところは、要求された量までしか計算できないことです。したがって、最初の2つのインデックスの要求は(ほぼ)同じくらい簡単です。

y = (i for i,v in enumerate(l) if is_odd(v))
x1 = next(y)
x2 = next(y)

ただし、最後のインデックスの後にStopIteration例外が発生することを期待してください(ジェネレーターの動作)。これは、そのような値が見つからなかったことを知るための「最初の」アプローチでも便利です--- list.index()関数はここでValueErrorをスローします。

83
Paul

@Paulの受け入れられた答えは最高ですが、ここでは主に娯楽と指導の目的のために、少し横向きの変形があります...

_>>> class X(object):
...   def __init__(self, pred): self.pred = pred
...   def __eq__(self, other): return self.pred(other)
... 
>>> l = [8,10,4,5,7]
>>> def is_odd(x): return x % 2 != 0
... 
>>> l.index(X(is_odd))
3
_

本質的に、Xの目的は、「平等」の意味を通常のものから「この述語を満たす」に変更することであり、それにより、平等のチェックとして定義されるあらゆる種類の状況で述語の使用を許可しますたとえば、if any(is_odd(x) for x in l):の代わりに、短いif X(is_odd) in l:などをコーディングすることもできます。

使用する価値がありますか? @Paulのようなより明示的なアプローチが同じように便利な場合(特に、古くて適切ではない_.next_メソッドではなく、新しい光沢のある組み込みnext関数を使用するように変更した場合、私はその答えへのコメントで提案しているように)、しかし、それ(または「平等の意味を微調整する」というアイデアの他の変形、および多分他のコンパレーターおよび/またはハッシュ)が適切であるかもしれない状況があります。ほとんどの場合、いつかゼロから発明する必要を避けるために、アイデアについて知る価値があります;-)。

12
Alex Martelli

可能性の1つは、組み込みの 列挙 関数です。

def index_of_first(lst, pred):
    for i,v in enumerate(lst):
        if pred(v):
            return i
    return None

「述語」として説明するような関数を参照するのが一般的です。何らかの質問に対してtrueまたはfalseを返します。そのため、この例ではpredと呼んでいます。

また、Noneを返す方が良い質問だと思います。それが質問に対する本当の答えだからです。呼び出し元は、必要に応じてNoneで爆発することを選択できます。

11

単一の機能ではありませんが、非常に簡単に実行できます。

>>> test = lambda c: c == 'x'
>>> data = ['a', 'b', 'c', 'x', 'y', 'z', 'x']
>>> map(test, data).index(True)
3
>>>

リスト全体を一度に評価したくない場合は、itertoolsを使用できますが、それほどきれいではありません。

>>> from itertools import imap, ifilter
>>> from operator import itemgetter
>>> test = lambda c: c == 'x'
>>> data = ['a', 'b', 'c', 'x', 'y', 'z']
>>> ifilter(itemgetter(1), enumerate(imap(test, data))).next()[0]
3
>>> 

ジェネレータ式を使用することは、おそらくitertoolsよりも読みやすいでしょう。

Python3では、mapfilterは遅延イテレータを返すことに注意してください。

from operator import itemgetter
test = lambda c: c == 'x'
data = ['a', 'b', 'c', 'x', 'y', 'z']
next(filter(itemgetter(1), enumerate(map(test, data))))[0]  # 3
4
Steve Losh

アレックスの答えのバリエーション。これにより、is_oddまたはいずれかの述語を使用するたびにXと入力する必要がなくなります。

>>> class X(object):
...     def __init__(self, pred): self.pred = pred
...     def __eq__(self, other): return self.pred(other)
... 
>>> L = [8,10,4,5,7]
>>> is_odd = X(lambda x: x%2 != 0)
>>> L.index(is_odd)
3
>>> less_than_six = X(lambda x: x<6)
>>> L.index(less_than_six)
2
2
John La Rooy

リスト内包表記でこれを行うことができます:

l = [8,10,4,5,7]
filterl = [a for a in l if a % 2 != 0]

その後、filterlは、式a%2!= 0を満たすリストのすべてのメンバーを返します。よりエレガントな方法と言えます...

1
Vincent Osinga