2010-11-26 43 views
11

Apparemment, ce problème arrive assez souvent, après avoir luanalyse syntaxique entre parenthèses imbriquées en python, le contenu grab par niveau

Regular expression to detect semi-colon terminated C++ for & while loops

et la réflexion sur le problème pendant un certain temps, je l'ai écrit une fonction pour renvoyer le contenu contenu à l'intérieur d'un nombre arbitraire d'imbriqués()

La fonction pourrait facilement être étendue à n'importe quel objet d'expression régulière, en postant ici pour vos pensées et considérations.

des conseils de refactoring serait apprécié

(note, je suis nouveau à python toujours, et n'a pas envie de trouver comment générant des exceptions ou autre chose, donc je viens d'avoir le retour de la fonction « échec » si elle couldin't comprendre ce qui se passait)

fonction modifiée pour prendre en compte les commentaires:

def ParseNestedParen(string, level): 
    """ 
    Return string contained in nested(), indexing i = level 
    """ 
    CountLeft = len(re.findall("\(", string)) 
    CountRight = len(re.findall("\)", string)) 
    if CountLeft == CountRight: 
     LeftRightIndex = [x for x in zip(
     [Left.start()+1 for Left in re.finditer('\(', string)], 
     reversed([Right.start() for Right in re.finditer('\)', string)]))] 

    elif CountLeft > CountRight: 
     return ParseNestedParen(string + ')', level) 

    elif CountLeft < CountRight: 
     return ParseNestedParen('(' + string, level) 

    return string[LeftRightIndex[level][0]:LeftRightIndex[level][1]] 

Répondre

23

Vous ne faites pas clairement exactement ce que la spécification de votre fonction est, mais ce comportement me semble mal à:

>>> ParseNestedParen('(a)(b)(c)', 0) 
['a)(b)(c'] 
>>> nested_paren.ParseNestedParen('(a)(b)(c)', 1) 
['b'] 
>>> nested_paren.ParseNestedParen('(a)(b)(c)', 2) 
[''] 

Autres commentaires sur votre code:

  • Docstring dit "générer", mais la fonction renvoie une liste, pas un générateur.
  • Puisqu'une seule chaîne est retournée, pourquoi la retourner dans une liste?
  • Dans quelles circonstances la fonction peut-elle renvoyer la chaîne fail?
  • Appeler à plusieurs reprises re.findall, puis jeter le résultat est un gaspillage.
  • Vous essayez de rééquilibrer les parenthèses dans la chaîne, mais vous le faites qu'une seule parenthèse à la fois:
>>> ParseNestedParen(')' * 1000, 1) 
RuntimeError: maximum recursion depth exceeded while calling a Python object 

Comme Thomi dit dans le question you linked to, « expressions régulières vraiment sont les mauvais outil pour le travail! "


La façon habituelle pour analyser des expressions imbriquées est d'utiliser une pile, le long de ces lignes:

def parenthetic_contents(string): 
    """Generate parenthesized contents in string as pairs (level, contents).""" 
    stack = [] 
    for i, c in enumerate(string): 
     if c == '(': 
      stack.append(i) 
     elif c == ')' and stack: 
      start = stack.pop() 
      yield (len(stack), string[start + 1: i]) 

>>> list(parenthetic_contents('(a(b(c)(d)e)(f)g)')) 
[(2, 'c'), (2, 'd'), (1, 'b(c)(d)e'), (1, 'f'), (0, 'a(b(c)(d)e)(f)g')] 
+0

Le comportement associé à ParseNestedParen ('(a) (b) (c)', 0) est réellement correct, mais ma fonction est le mauvais outil pour le travail, j'ai écrit la fonction avec string = "some_function (another_function (some_argument)) " à l'esprit. pourquoi retourner dans une liste? ne devrait pas avoir. bon point, merci! quand vais-je retourner échouer? Je ne sais pas. peut-être jamais. est-il à partir de quand je codais la fonction appel réitéré trouver tout est gaspillage? Donc devrais-je juste faire la liste countparen = [re.findall (str) pour str en ["\ (", "\)"]] et l'utiliser à la place? comment sinon rééquilibrer la parenthèse? merci pour les commentaires! –

+0

Il est difficile de dire quelle est la bonne chose à faire à propos des parenthèses déséquilibrées, parce que je ne sais pas à quoi la fonction va servir. Les chaînes les plus susceptibles d'être déséquilibrées sont une sorte d'erreur d'entrée, et doivent être soit ignorées (pour des applications comme la coloration syntaxique), soit élevées comme des erreurs (pour des applications comme la compilation). –

0
#!/usr/bin/env python 
import re 

def ParseNestedParen(string, level): 
    """ 
    Generate strings contained in nested(), indexing i = level 
    """ 
    if len(re.findall("\(", string)) == len(re.findall("\)", string)): 
     LeftRightIndex = [x for x in zip(
     [Left.start()+1 for Left in re.finditer('\(', string)], 
     reversed([Right.start() for Right in re.finditer('\)', string)]))] 

    elif len(re.findall("\(", string)) > len(re.findall("\)", string)): 
     return ParseNestedParen(string + ')', level) 

    elif len(re.findall("\(", string)) < len(re.findall("\)", string)): 
     return ParseNestedParen('(' + string, level) 

    else: 
     return 'fail' 

    return [string[LeftRightIndex[level][0]:LeftRightIndex[level][1]]] 

Tests:

if __name__ == '__main__': 

    teststring = "outer(first(second(third)second)first)outer" 

    print(ParseNestedParen(teststring, 0)) 
    print(ParseNestedParen(teststring, 1)) 
    print(ParseNestedParen(teststring, 2)) 

    teststring_2 = "outer(first(second(third)second)" 

    print(ParseNestedParen(teststring_2, 0)) 
    print(ParseNestedParen(teststring_2, 1)) 
    print(ParseNestedParen(teststring_2, 2)) 

    teststring_3 = "second(third)second)first)outer" 

    print(ParseNestedParen(teststring_3, 0)) 
    print(ParseNestedParen(teststring_3, 1)) 
    print(ParseNestedParen(teststring_3, 2)) 

sortie:

Running tool: python3.1 

['first(second(third)second)first'] 
['second(third)second'] 
['third'] 
['first(second(third)second)'] 
['second(third)second'] 
['third'] 
['(second(third)second)first'] 
['second(third)second'] 
['third'] 
>>> 
+0

Donc, comme vous pouvez le constater, la fonction permet entre parenthèses déséquilibrées, mais pas d'une manière très élégante . –