Jak mogę podzielić ciąg w liście?

głosy
36

Jeśli mam ten ciąg:

2 + 24 * 48/32

co jest najbardziej efektywne podejście do tworzenia tej listy:

[ '2', '+', '24', '*', '48' '/', '32']

Utwórz 18/09/2008 o 00:17
źródło użytkownik
W innych językach...                            


12 odpowiedzi

głosy
50

Tak się składa, że tokeny chcesz podzielone są już tokeny Python, dzięki czemu można korzystać z wbudowanego tokenizemodułu. To prawie jedna wkładka:

from cStringIO import StringIO
from tokenize import generate_tokens
STRING = 1
list(token[STRING] for token 
     in generate_tokens(StringIO('2+24*48/32').readline)
     if token[STRING])
['2', '+', '24', '*', '48', '/', '32']
Odpowiedział 21/09/2008 o 17:25
źródło użytkownik

głosy
36

Można korzystać splitz remodułu.

re.split (pattern, string, maxsplit = 0, flagi = 0)

Podzielonego przez ciąg wystąpień wzorca. Jeśli przechwytywania nawiasach są wykorzystywane w strukturze, a treść wszystkich grupach we wzorze są zwrócone w ramach otrzymanego wykazu.

Przykład Kod:

import re
data = re.split(r'(\D)', '2+24*48/32')

\RE

Gdy flaga UNICODE nie jest określona, ​​\ D dopasowuje dowolny niż cyfra; jest to równoznaczne z ustawionymi ^ [0-9].

Odpowiedział 18/09/2008 o 00:25
źródło użytkownik

głosy
18
>>> import re
>>> re.findall(r'\d+|\D+', '2+24*48/32=10')

['2', '+', '24', '*', '48', '/', '32', '=', '10']

Mecze kolejnych cyfr lub kolejnych non-cyfr.

Każdy mecz jest zwracany jako nowy element na liście.

W zależności od zastosowania, może trzeba zmienić wyrażenie regularne. Takie jakby trzeba dopasować numerów z przecinkiem.

>>> re.findall(r'[0-9\.]+|[^0-9\.]+', '2+24*48/32=10.1')

['2', '+', '24', '*', '48', '/', '32', '=', '10.1']
Odpowiedział 18/09/2008 o 03:39
źródło użytkownik

głosy
18

To wygląda na problem analizowania, a więc jestem zmuszony do przedstawienia rozwiązania w oparciu o techniki analizowania.

Choć może się wydawać, że chcesz „podzielić” to ciąg znaków, myślę, że to, co rzeczywiście chcesz zrobić, to „tokenize” it. Tokenizacja lub lexxing krokiem jest kompilacja przed analizą. Mam zmienione mój oryginalny przykład w edycji do wdrożenia odpowiedniego rekurencyjną godnej parsera tutaj. Jest to najprostszy sposób, aby wdrożyć parser ręcznie.

import re

patterns = [
    ('number', re.compile('\d+')),
    ('*', re.compile(r'\*')),
    ('/', re.compile(r'\/')),
    ('+', re.compile(r'\+')),
    ('-', re.compile(r'\-')),
]
whitespace = re.compile('\W+')

def tokenize(string):
    while string:

        # strip off whitespace
        m = whitespace.match(string)
        if m:
            string = string[m.end():]

        for tokentype, pattern in patterns:
            m = pattern.match(string)
            if m:
                yield tokentype, m.group(0)
                string = string[m.end():]

def parseNumber(tokens):
    tokentype, literal = tokens.pop(0)
    assert tokentype == 'number'
    return int(literal)

def parseMultiplication(tokens):
    product = parseNumber(tokens)
    while tokens and tokens[0][0] in ('*', '/'):
        tokentype, literal = tokens.pop(0)
        if tokentype == '*':
            product *= parseNumber(tokens)
        elif tokentype == '/':
            product /= parseNumber(tokens)
        else:
            raise ValueError("Parse Error, unexpected %s %s" % (tokentype, literal))

    return product

def parseAddition(tokens):
    total = parseMultiplication(tokens)
    while tokens and tokens[0][0] in ('+', '-'):
        tokentype, literal = tokens.pop(0)
        if tokentype == '+':
            total += parseMultiplication(tokens)
        elif tokentype == '-':
            total -= parseMultiplication(tokens)
        else:
            raise ValueError("Parse Error, unexpected %s %s" % (tokentype, literal))

    return total

def parse(tokens):
    tokenlist = list(tokens)
    returnvalue = parseAddition(tokenlist)
    if tokenlist:
        print 'Unconsumed data', tokenlist
    return returnvalue

def main():
    string = '2+24*48/32'
    for tokentype, literal in tokenize(string):
        print tokentype, literal

    print parse(tokenize(string))

if __name__ == '__main__':
    main()

Implementacja obsługi nawiasach pozostawiamy jako ćwiczenie dla czytelnika. Ten przykład będzie poprawnie wykonać mnożenie przed dodaniem.

Odpowiedział 18/09/2008 o 00:54
źródło użytkownik

głosy
6

Jest to problem parsowania, więc ani nie regex split () są „dobre” rozwiązanie. Użyć generatora parsera zamiast.

Chciałbym przyjrzeć pyparsing . Odnotowano również kilka przyzwoitych artykuły o pyparsing w Python Magazine .

Odpowiedział 19/09/2008 o 08:37
źródło użytkownik

głosy
5

y = "2 + 24 * 48/32"

P = re.compile (R '(\ w +)')

p.split (e)

Odpowiedział 18/09/2008 o 00:25
źródło użytkownik

głosy
4

Innym rozwiązaniem byłoby, aby uniknąć pisania kalkulatora takiego w ogóle. Napisanie parsera RPN jest znacznie prostsze, a nie ma żadnej z wrodzonej dwuznaczności w formie pisemnej z matematyki notacji Infix.

import operator, math
calc_operands = {
    '+': (2, operator.add),
    '-': (2, operator.sub),
    '*': (2, operator.mul),
    '/': (2, operator.truediv),
    '//': (2, operator.div),
    '%': (2, operator.mod),
    '^': (2, operator.pow),
    '**': (2, math.pow),
    'abs': (1, operator.abs),
    'ceil': (1, math.ceil),
    'floor': (1, math.floor),
    'round': (2, round),
    'trunc': (1, int),
    'log': (2, math.log),
    'ln': (1, math.log),
    'pi': (0, lambda: math.pi),
    'e': (0, lambda: math.e),
}

def calculate(inp):
    stack = []
    for tok in inp.split():
        if tok in self.calc_operands:
            n_pops, func = self.calc_operands[tok]
            args = [stack.pop() for x in xrange(n_pops)]
            args.reverse()
            stack.append(func(*args))
        elif '.' in tok:
            stack.append(float(tok))
        else:
            stack.append(int(tok))
    if not stack:
        raise ValueError('no items on the stack.')
    return stack.pop()
    if stack:
        raise ValueError('%d item(s) left on the stack.' % len(stack))

calculate('24 38 * 32 / 2 +')
Odpowiedział 18/09/2008 o 04:07
źródło użytkownik

głosy
4

Wyrażenia regularne:

>>> import re
>>> splitter = re.compile(r'([+*/])')
>>> splitter.split("2+24*48/32")

Można rozszerzyć wyrażenie regularne zawierać żadnych innych znaków, które mają być podzielone na.

Odpowiedział 18/09/2008 o 00:21
źródło użytkownik

głosy
1
>>> import re
>>> my_string = "2+24*48/32"
>>> my_list = re.findall(r"-?\d+|\S", my_string)
>>> print my_list

['2', '+', '24', '*', '48', '/', '32']

Będzie to rade. zanim ja spotkałem tego rodzaju problemu.

Odpowiedział 14/01/2012 o 17:21
źródło użytkownik

głosy
0

To nie jest odpowiedź na pytanie, dokładnie, ale wierzę, że to rozwiązuje co próbujesz osiągnąć. Dodam go jako komentarz, ale nie mam na to zgody jeszcze.

Ja osobiście skorzystać z matematyki funkcje Pythona bezpośrednio z exec:

Wyrażenie = "2 + 24 * 48/32"
Exec "wynik =" + ekspresji
druku wynik
38

Odpowiedział 19/08/2010 o 01:38
źródło użytkownik

głosy
0

Dlaczego nie wystarczy użyć SymPy ? Należy robić to, co starasz się osiągnąć.

Odpowiedział 19/09/2008 o 04:22
źródło użytkownik

głosy
0

Jestem pewien, Tim oznaczało

splitter = re.compile(r'([\D])'). 

jeśli skopiować dokładnie to, co ma pan na dół tylko dostać digitsnie operators.

Odpowiedział 18/09/2008 o 01:45
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more