返回列表中第一个元素的索引,该元素使传递的函数为真
- 2025-03-18 08:54:00
- admin 原创
- 48
问题描述:
该list.index(x)
函数返回列表中第一个值为 的项目的索引x
。
是否有一个函数list_func_index()
,类似于index()
具有函数f()
作为参数的函数。函数对列表的 f()
每个元素 运行,直到返回。然后返回 的索引。e
`f(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
有没有更优雅的解决方案?(以及更好的函数名称)
解决方案 1:
您可以使用生成器在一行中完成此操作:
next(i for i,v in enumerate(l) if is_odd(v))
生成器的优点在于它们只计算请求的数量。因此请求前两个索引(几乎)同样简单:
y = (i for i,v in enumerate(l) if is_odd(v))
x1 = next(y)
x2 = next(y)
不过,在最后一个索引之后会出现 StopIteration 异常(这就是生成器的工作方式)。这在“先取”方法中也很方便,可以知道没有找到这样的值 ---list.index()
函数会ValueError
在此处引发。
解决方案 2:
一种可能性是内置的枚举函数:
def index_of_first(lst, pred):
for i, v in enumerate(lst):
if pred(v):
return i
return None
通常将您描述的函数称为“谓词”;它会针对某些问题返回 true 或 false。这就是我pred
在示例中这样称呼它的原因。
我还认为返回 会更好None
,因为这是问题的真正答案。None
如果需要,调用者可以选择在 上展开。
解决方案 3:
保罗接受的答案是最好的,但这里有一个小小的横向思维变体,主要是为了娱乐和指导目的......:
>>> 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
方法时,正如我在该答案的评论中所建议的那样),但还有其他情况下它(或“调整相等的含义”这一想法的其他变体,也许还有其他比较器和/或散列)可能是合适的。最重要的是,值得了解这个想法,以避免有一天不得不从头开始发明它。
解决方案 4:
虽然不是一个单一的函数,但是你可以在 Python 2 中很容易地做到这一点:
>>> 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
>>> ifilter(itemgetter(1), enumerate(imap(test, data))).next()[0]
3
尽管仅使用生成器表达式可能更具可读性itertools
。
请注意在 Python3 中,map
返回filter
惰性迭代器,您只需使用:
>>> from operator import itemgetter
>>> next(filter(itemgetter(1), enumerate(map(test, data))))[0]
3
解决方案 5:
Alex 的答案的变体。这样就避免了X
每次要使用is_odd
或任何谓词时都必须键入
>>> 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
解决方案 6:
直观的单行解决方案:
i = list(map(lambda value: value > 0, data)).index(True)
解释:
我们使用 map 函数创建一个包含 True 或 False 的迭代器,具体取决于列表中的每个元素是否符合 lambda 中的条件。
然后我们将地图输出转换为列表
然后使用索引函数,我们得到第一个真的索引,它与通过条件的第一个值的索引相同。
解决方案 7:
使用一些我最喜欢的、值得更多喜爱的工具(indexOf
,,compress
)count
:
from operator import indexOf
def list_func_index(lst, func):
return indexOf(map(func, lst), True)
from itertools import compress, count
def list_func_index(lst, func):
return next(compress(count(), map(func, lst)))
或者与你的ValueError
from itertools import compress, count
def list_func_index(lst, func):
for index in compress(count(), map(func, lst)):
return index
raise ValueError('no element making func True')
ist_func_index(l, is_odd))
在线尝试!
解决方案 8:
这是一个 python3 答案,旨在体现 any() 和 all() 的精神:
def index(iterable, desired_item=True):
for i,item in enumerate(iterable):
if item == desired_item:
return i
raise ValueError(f"no {desired_item} in iterable")
>>> any(n!=0 and n%6==0 and n%15==0 for n in range(10**100))
True
>>> all(n!=0 and n%6==0 and n%15==0 for n in range(10**100))
False
>>> index(n!=0 and n%6==0 and n%15==0 for n in range(10**100))
30
使用OP的示例:
>>> l = [8,10,4,5,7]
>>> def is_odd(x): return x % 2 != 0
>>> any(is_odd(x) for x in l)
True
>>> all(is_odd(x) for x in l)
False
>>> index(is_odd(x) for x in l)
3
注意:尽管我通常不喜欢默认参数,但我还是添加了可选desired_item
参数,这只是为了减轻使用名称的负罪感index
;这使得它对于那些认为该函数可以像下面这样使用的人来说很有用(因为他们熟悉index
许多语言中调用的其他函数,包括list
python 中的成员函数):
>>> index((2*n for n in range(googol)), 20)
10
解决方案 9:
你可以用列表理解来做到这一点:
l = [8,10,4,5,7]
filterl = [a for a in l if a % 2 != 0]
然后 filterl 将返回满足表达式 a % 2 != 0 的列表中的所有成员。我想说一种更优雅的方法......
扫码咨询,免费领取项目管理大礼包!