2009-11-09 2 views
30

La fonction list.index(x) renvoie l'index dans la liste du premier élément dont la valeur est x.Python: retourne l'index du premier élément d'une liste qui rend une fonction passée true

est-il une fonction, list_func_index(), similaire à la fonction index() qui a une fonction, f(), en tant que paramètre. la fonction, f() est exécutée sur chaque élément, e, de la liste jusqu'au f(e) renvoie True. puis list_func_index() renvoie l'index de e.

codewise:

>>> 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 

est-il une solution plus élégante? (Et un meilleur nom pour la fonction)

Répondre

54

Vous pouvez le faire que dans des générateurs utilisant un liner:

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

La bonne chose à propos des générateurs est qu'ils ne calculent à hauteur du montant demandé. Donc, demander les deux premiers indices est (presque) tout aussi facile:

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

Bien, attendez-vous une exception StopIteration après le dernier indice (qui est le fonctionnement des générateurs). Ceci est également pratique dans votre approche "à prendre en premier", pour savoir qu'aucune valeur de ce type n'a été trouvée --- la fonction list.index() lancerait ValueError ici.

supplémentaire Un commentaire à noter par Alex Martelli: En Python 2.6 et plus, next(someiterator) est la manière nouvelle et privilégiée sur someiterator.next().

+0

+1 ce que j'étais en train de taper :-) – bobince

+7

Wow, une autre victoire pour l'obscurité. Il n'y a rien à gagner à utiliser un code "intelligent" comme celui-ci. –

+0

@JF: en fait c'est presque une transcription alphabétisée de votre solution, seulement avec des accolades au lieu de "def". aussi la valeur de retour est en avant, au lieu de à la fin (un avantage, je dirais). et il y a même la possibilité de prendre plusieurs réponses, ce que votre solution ne peut pas faire. Alors, quelle est exactement la raison pour laquelle vous avez baissé la réponse de votre concurrent? – Paul

8

Une possibilité est intégrée enumerate fonction:

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

Il est typique pour désigner une fonction comme celle que vous décrivez comme un « prédicat »; il renvoie vrai ou faux pour une question. C'est pourquoi je l'appelle pred dans mon exemple.

Je pense aussi qu'il serait préférable de retourner None, puisque c'est la vraie réponse à la question. L'appelant peut choisir d'exploser sur None, si nécessaire.

+1

plus élégante, mieux nommée, en effet – bandana

+0

Je pense que l'OP voulait imiter le comportement de l'indice d'élever ValueError si la valeur donnée est introuvable. – PaulMcG

+0

+1 pour énumérer ce qui est un grand favori du mien. Je ne me souviens pas de la dernière fois où j'ai dû maintenir une variable d'index, l'ancienne façon C en python. –

0

vous pouvez le faire avec une liste-compréhension:

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

Alors filterl retournera tous les membres de la liste remplissant l'expression d'un% 2 = 0. Je dirais une méthode plus élégante ...!

+0

Pouvez-vous modifier votre réponse pour qu'elle ressemble davantage à la fonction de l'OP qui a une liste et une fonction en tant que paramètres? – quamrana

+4

C'est faux. Il renvoie une liste de valeurs, pas un seul index. – recursive

+0

filterl = [a pour un dans l si is_odd (a)] –

3

pas une seule fonction, mais vous pouvez le faire assez facilement:

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

Si vous ne voulez pas évaluer la liste entière à la fois, vous pouvez utiliser itertools, mais ce n'est pas aussi jolie: 01 L'utilisation d'une expression génératrice est probablement plus lisible que celle d'un générateur.

+0

Malheureusement, cela évalue la liste entière - serait agréable d'avoir une solution qui court-circuite, c'est-à-dire, retourne immédiatement lors de la recherche de la première correspondance. – PaulMcG

+0

Cela pourrait être fait avec une compréhension de générateur. – recursive

9

@ La réponse de Paul acceptée est le meilleur, mais voici une petite variante-pensée latérale, la plupart du temps à des fins de divertissement et d'instruction ...:

>>> 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 

essentiellement, le but de X est de changer le sens de " égalité "du normal à" satisfait ce prédicat ", permettant ainsi l'utilisation de prédicats dans toutes sortes de situations qui sont définies comme vérification de l'égalité - par exemple, il vous permet également de coder, au lieu de if any(is_odd(x) for x in l):, le plus court if X(is_odd) in l: , et ainsi de suite.

Cela vaut la peine d'utiliser? Pas quand une approche plus explicite comme celle adoptée par @Paul est tout aussi pratique (surtout quand elle est modifiée pour utiliser la nouvelle fonction brillante intégrée next plutôt que la méthode .next plus ancienne et moins appropriée, comme je le suggère dans un commentaire à cette réponse), mais il y a d'autres situations où il (ou d'autres variantes de l'idée "modifier la signification de l'égalité", et peut-être d'autres comparateurs et/ou hachage) peut être approprié. Surtout, il vaut la peine de connaître l'idée, pour éviter de devoir l'inventer à partir de zéro un jour ;-).

+0

Nice one! Mais que pourrions-nous "nommer" X? Quelque chose comme "Key" peut-être? Parce que ça me rappelle l.sort (clé = fn). – Paul

+0

Vous pourriez presque l'appeler "Equals", donc la ligne lit l.index (Equals (is_odd)) – tgray

+1

Je pense que ce que Alex (implicitement) a suggéré, 'Satisfies', est un bon nom pour cela. –

1

Une variante de la réponse d'Alex. Cela évite d'avoir à taper X chaque fois que vous souhaitez utiliser is_odd ou selon prédicat

>>> 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 
Questions connexes