2010-01-25 13 views
7

Chcę być w stanie wyciągnąć typ i liczbę liter z fragmentu tekstu, gdzie litery mogą być w dowolnej kolejności. Jest jeszcze kilka innych analiz, nad którymi pracuję, ale ten kawałek mnie zaskoczył!Pyparsing - gdzie kolejność tokenów nieprzewidywalnych

input -> result 
"abc" -> [['a',1], ['b',1],['c',1]] 
"bbbc" -> [['b',3],['c',1]] 
"cccaa" -> [['a',2],['c',3]] 

Mogę użyć wyszukiwania lub skanowania i powtórzyć dla każdej możliwej litery, ale czy istnieje czysty sposób to zrobić?

To ile mam:

from pyparsing import * 


def handleStuff(string, location, tokens): 

     return [tokens[0][0], len(tokens[0])] 


stype = Word("abc").setParseAction(handleStuff) 
section = ZeroOrMore(stype("stype")) 


print section.parseString("abc").dump() 
print section.parseString("aabcc").dump() 
print section.parseString("bbaaa").dump() 

Odpowiedz

6

I nie było jasne, z opisu czy znaki wejściowe mogą być mieszane jak „ababc”, ponieważ we wszystkich przypadków testowych, litery były zawsze grupowane. Jeśli litery zawsze zgrupowane razem, można użyć tego kodu pyparsing:

def makeExpr(ch): 
    expr = Word(ch).setParseAction(lambda tokens: [ch,len(tokens[0])]) 
    return expr 

expr = Each([Optional(makeExpr(ch)) for ch in "abc"]) 

for t in tests: 
    print t,expr.parseString(t).asList() 

Każdy konstrukt dba o dopasowanie poza kolejnością i Word (ch) obsługuje powtórzenia 1-do-N. Funkcja parsowania zajmuje się przekształceniem sparsowanych tokenów w krotki (znak, policz).

+0

Tak, znaki są zgrupowane, więc jest to idealne. Dzięki za rozwiązanie i wyjaśnienie. Kochanie pyparsing! – PhoebeB

6

Jedno rozwiązanie:

text = 'sufja srfjhvlasfjkhv lasjfvhslfjkv hlskjfvh slfkjvhslk' 
print([(x,text.count(x)) for x in set(text)]) 

Nie pyparsing zaangażowany, ale wydaje się przesadą.

+0

Myślę, że pójdę z tym za pomocą pyparsing, aby pobrać fragment i setParseAction, aby go przetworzyć z tym. Wciąż jednak chciałbyś wiedzieć, czy istnieje rozwiązanie typu "pyparsing"! – PhoebeB

+0

Dziękuję za rozwiązanie - zostało ono teraz wkomponowane w pipsy, ale dzięki za pomoc i bardzo miłe rozwiązanie! – PhoebeB

1

pyparsing siebie - w Pythonie 3.1, collections.Counter sprawia takie zadania liczenia naprawdę łatwe. Dobra wersja Counter dla Python 2 można znaleźć here.

+0

Podobnie jak klasa Counter - będzie o tym pamiętać w przypadku innych rzeczy. Dzięki. – PhoebeB

2

Jeśli chcesz podejścia czystej pyparsing, to czuje się o prawo:

from pyparsing import * 

# lambda to define expressions 
def makeExpr(ch): 
    expr = Literal(ch).setResultsName(ch, listAllMatches=True) 
    return expr 

expr = OneOrMore(MatchFirst(makeExpr(c) for c in "abc")) 
expr.setParseAction(lambda tokens: [[a,len(b)] for a,b in tokens.items()]) 


tests = """\ 
abc 
bbbc 
cccaa 
""".splitlines() 

for t in tests: 
    print t,expr.parseString(t).asList() 

Reprodukcje :

abc [['a', 1], ['c', 1], ['b', 1]] 
bbbc [['c', 1], ['b', 3]] 
cccaa [['a', 2], ['c', 3]] 

Ale to zaczyna docierać do niejasnego obszaru kodu, ponieważ opiera się na kilku bardziej tajemniczych funkcjach pyparsingu. Ogólnie lubię liczniki częstotliwości, które używają defaultdict (jeszcze nie próbowałem Counter), ponieważ jest całkiem jasne, co robisz.

Powiązane problemy