2009-10-18 4 views
1

dire que nous avons une à plusieurs couches itérables avec des chaînes au niveau « final », oui chaînes sont itératives, mais je pense que vous obtenez mon sens:Grep itérables à couches multiples pour les chaînes qui correspondent à (Python)

['something', 
('Diff', 
('diff', 'udiff'), 
('*.diff', '*.patch'), 
('text/x-diff', 'text/x-patch')), 

('Delphi', 
('delphi', 'pas', 'pascal', 'objectpascal'), 
('*.pas',), 
('text/x-pascal',['lets', 'put one here'],)), 

('JavaScript+Mako', 
('js+mako', 'javascript+mako'), 
('application/x-javascript+mako', 
'text/x-javascript+mako', 
'text/javascript+mako')), 
... 
] 

Y at-il un moyen pratique que je pourrais mettre en œuvre une recherche qui me donnerait les indices des chaînes correspondantes? Je voudrais quelque chose qui agirait quelque chose comme ça (où la liste ci-dessus est data):

>>> grep('javascript', data) 

et ce serait revenir [(2,1,1), (2,2,0), (2, 2,1), (2,2,2)] peut-être. Il me manque peut-être une solution comparable qui ne renvoie rien de ce genre mais qui peut m'aider à trouver des chaînes dans une liste multi-couches d'itérations des itérations de .... chaînes.

J'ai écrit un peu mais il semblait juvénile et inélégant alors j'ai pensé que je demanderais ici. Je suppose que je pourrais continuer à imbriquer l'exception de la façon dont j'ai commencé ici au nombre de niveaux que la fonction supporterait alors, mais j'espérais obtenir quelque chose de net, abstrait, pythonique.

import re 

def rgrep(s, data): 
    ''' given a iterable of strings or an iterable of iterables of strings, 

    returns the index/indices of strings that contain the search string. 

    Args:: 

     s - the string that you are searching for 
     data - the iterable of strings or iterable of iterables of strings 
    ''' 


    results = [] 
    expr = re.compile(s) 
    for item in data: 
     try: 
      match = expr.search(item) 
      if match != None: 
       results.append(data.index(item)) 

     except TypeError: 
      for t in item: 
       try: 
        m = expr.search(t) 
        if m != None: 
         results.append((list.index(item), item.index(t))) 

       except TypeError: 
        ''' you can only go 2 deep! ''' 
        pass 

    return results 
+0

Il serait utile que vous montriez ce que vous avez déjà fait et quelle partie de votre propre solution vous pose problème. –

Répondre

3

Je partagerai énumération récursive de grep:

def enumerate_recursive(iter, base=()): 
    for index, item in enumerate(iter): 
     if isinstance(item, basestring): 
      yield (base + (index,)), item 
     else: 
      for pair in enumerate_recursive(item, (base + (index,))): 
       yield pair 

def grep_index(filt, iter): 
    return (index for index, text in iter if filt in text) 

De cette façon, vous pouvez faire les deux Grepping non récurrent et récursif:

l = list(grep_index('opt1', enumerate(sys.argv))) # non-recursive 
r = list(grep_index('diff', enumerate_recursive(your_data))) # recursive 

Notez également que nous utilisons ici, en économisant de la RAM pour des séquences plus longues si nécessaire.

Une solution encore plus générique serait de donner un appelable au lieu de chaîne à grep_index. Mais cela pourrait ne pas être nécessaire pour vous.

+0

J'aime mieux ta solution que la mienne :) – unutbu

+0

oui, c'est bon, merci. –

+0

Note: la partie 'for pair in ...: yield 'pourrait être remplacée par une construction' yield from' dans les nouvelles implémentations python. – liori

0

Pour obtenir la position utiliser enumerate()

>>> data = [('foo', 'bar', 'frrr', 'baz'), ('foo/bar', 'baz/foo')] 
>>> 
>>> for l1, v1 in enumerate(data): 
...  for l2, v2 in enumerate(v1): 
...    if 'f' in v2: 
...      print l1, l2, v2 
... 
0 0 foo 
1 0 foo/bar 
1 1 baz/foo 

Dans cet exemple, je me sers d'un simple match 'foo' in bar mais vous utilisez probablement regex pour le travail.

De toute évidence, enumerate() peut prendre en charge plus de 2 niveaux comme dans votre publication éditée.

1

Voici un grep qui utilise la récursivité pour rechercher la structure de données.

Notez que de bonnes structures de données ouvrent la voie à des solutions élégantes. Les mauvaises structures de données vous font pencher en arrière pour s'adapter. Cela ressemble à l'un de ces cas où une mauvaise structure de données entrave plutôt que de vous aider. Avoir une structure de données simple avec une structure plus uniforme (au lieu d'utiliser ce grep) pourrait valoir la peine d'être étudiée.

#!/usr/bin/env python 

data=['something', 
('Diff', 
('diff', 'udiff'), 
('*.diff', '*.patch'), 
('text/x-diff', 'text/x-patch',['find','java deep','down'])), 

('Delphi', 
('delphi', 'pas', 'pascal', 'objectpascal'), 
('*.pas',), 
('text/x-pascal',['lets', 'put one here'],)), 

('JavaScript+Mako', 
('js+mako', 'javascript+mako'), 
('application/x-javascript+mako', 
'text/x-javascript+mako', 
'text/javascript+mako')), 
] 

def grep(astr,data,prefix=[]): 
    result=[] 
    for idx,elt in enumerate(data): 
     if isinstance(elt,basestring): 
      if astr in elt: 
       result.append(tuple(prefix+[idx])) 
     else: 
      result.extend(grep(astr,elt,prefix+[idx])) 
    return result 

def pick(data,idx): 
    if idx: 
     return pick(data[idx[0]],idx[1:]) 
    else: 
     return data 
idxs=grep('java',data) 
print(idxs) 
for idx in idxs: 
    print('data[%s] = %s'%(idx,pick(data,idx))) 
+0

Pourquoi pas isinstance (elt, basetring)? – liori

+0

Merci, je ne connaissais pas les basestring! – unutbu

Questions connexes