Je dois rechercher le premier, le dernier, l'un ou l'ensemble des occurrences de quelque chose dans quelque chose d'autre. Pour éviter de me répéter (DRY) je suis venu avec la solution suivante. Il est intéressant d'utiliser les méthodes search_revisions()
et collect_one_occurence()
des deux classes Searcher
.Abandonner le rendement pour éviter la condition dans la boucle
Dans SearcherYield
Je crée un générateur dans search_revisions()
seulement pour abandonner le générateur dans collect_one_occurence()
après avoir recueilli le premier résultat. Dans SearcherCondition
j'ai mis une condition dans la boucle. Cette condition devra être vérifiée pour chaque itération de la boucle.
Je ne peux pas décider si mon (ab) l'utilisation du rendement et l'abandon subséquent du générateur est un coup de génie ou un hack hideux. Qu'est-ce que tu penses? Avez-vous d'autres idées pour une telle situation?
#!/usr/bin/python
class Revision:
# a revision is something like a textfile.
# the search() method will search the textfile
# and return the lines which match the given pattern.
# for demonstration purposes this class is simplified
# to return predefined results
def __init__(self, results):
self.results = results
def search(self, pattern):
return self.results
class AbstractSearcher:
def __init__(self, revisions):
self.revisions = revisions
def search_for_first_occurence(self, pattern):
keys = sorted(self.revisions.iterkeys())
return self.collect_one_occurence(keys, pattern)
def search_for_last_occurence(self, pattern):
keys = sorted(self.revisions.iterkeys(), reverse = True)
return self.collect_one_occurence(keys, pattern)
def search_for_any_occurence(self, pattern):
keys = self.revisions.iterkeys()
return self.collect_one_occurence(keys, pattern)
def search_for_all_occurences(self, pattern):
keys = self.revisions.iterkeys()
return self.collect_all_occurences(keys, pattern)
class SearcherYield(AbstractSearcher):
def search_revisions(self, keys, pattern):
# create generator which yields the results one by one
for key in keys:
rev = self.revisions[key]
result = rev.search(pattern)
if result:
yield result
def collect_one_occurence(self, keys, pattern):
# take the first result and then abandon the generator
for result in self.search_revisions(keys, pattern):
return result
return []
def collect_all_occurences(self, keys, pattern):
# collect all results from generator
results = []
for result in self.search_revisions(keys, pattern):
results.extend(result)
return results
class SearcherCondition(AbstractSearcher):
def search_revisions(self, keys, pattern, just_one):
# collect either all results from all revisions
# or break the loop after first result found
results = []
for key in keys:
rev = self.revisions[key]
result = rev.search(pattern)
if result:
results.extend(result)
if just_one:
break
return results
def collect_one_occurence(self, keys, pattern):
return self.search_revisions(keys, pattern, just_one = True)
def collect_all_occurences(self, keys, pattern):
return self.search_revisions(keys, pattern, just_one = False)
def demo(searcher):
print searcher.__class__.__name__
print 'first:', searcher.search_for_first_occurence('foo')
print 'last: ', searcher.search_for_last_occurence('foo')
print 'any: ', searcher.search_for_any_occurence('foo')
print 'all: ', searcher.search_for_all_occurences('foo')
def main():
revisions = {
1: Revision([]),
2: Revision(['a', 'b']),
3: Revision(['c']),
4: Revision(['d','e', 'f']),
5: Revision([])}
demo(SearcherYield(revisions))
demo(SearcherCondition(revisions))
if __name__ == '__main__':
main()
Un contexte: les révisions sont essentiellement des fichiers texte. Vous pouvez penser à eux comme les révisions d'une page wiki. Typiquement, il y a des centaines de révisions, parfois des milliers. Chaque révision contient jusqu'à des milliers de lignes de texte. Il y a aussi des cas où il y a juste quelques révisions avec quelques lignes chacune.
Une recherche dans une révision recherche un motif dans le texte et renvoie les lignes correspondantes. Parfois, il y a des milliers de résultats, parfois il n'y a pas de résultats.
Parfois, j'ai juste besoin de savoir s'il y a des résultats dans n'importe quelle révision (cherchez-en). Parfois, je dois collecter tous les résultats pour un traitement ultérieur (recherche de tous). Parfois, j'ai juste besoin de la première révision avec une correspondance, parfois juste la dernière révision (recherche du premier et du dernier).
Ceci est waaaaaaay trop compliqué. Je ne peux pas vous dire comment y remédier à moins de pouvoir fournir un contexte plus utile, cependant; Tout ce que je peux obtenir de votre échantillon, c'est que vous avez écrit trop de code. Que cherches-tu, dans quoi? – katrielalex
Vous avez besoin d'une greffe de terminologie: ce que vous appelez first/last est vraiment une clé minimum/maximum et faire (en effet) 'trié (iterable) [0]' au lieu de 'min (iterable)' est un peu boggler. –
@JohnMachin: relire le code. le code ne fait pas 'trié (itérable) [0]'. la première révision avec une correspondance n'est pas nécessairement la première révision de la liste triée. – lesmana