简体   繁体   中英

Recursive math operations with string parsing

I want to parse a input string recursively in order to solve a mathematical problem.

The input is given in this form:

(P1=[X=(0.6,3),Y=(0.4,-1)],P2=[X=(0.3,0),Y=(0.4,[Y1=(0.8,[X=(0.2,1),Y=(0.8,2)]),Y2=(0.2,3)]),Z=(0.3,2)],P3=[A=(1,1)])

The objective is to calculate P1 , P2 and P3 and find who has the greatest value.

The mathematical resolution of this problem would be something like this:

P1 = 0.6 * 3 + 0.4 * (-1) = 1.4
P2 = 0.3 * 0 + 0.4 * ( 0.8 * (0.2 * 1 + 0.8 * 2) + 0.2 * 3) + 0.3 * 2 = 1.416
P3 = 1 * 1 = 1

So, P2 > P1 > P3

The output should be P2 .

This is a simplistic problem it could have a lot more of operations inside of parentheses.

I don't know how I can split the input string with different delimiters and how to do it recursively.

I'm new at programming and python but any help would be appreciated.

Writing a recursive parser is pretty ambitious for someone new to programming. A best-practice when writing any parser is to start by writing a BNF (which stands for "Backus-Naur Form"), which serves as a plan for what the bits of the parser will be. DO THIS BEFORE WRITING ANY CODE. Writing code and designing parsers are very different mental activities - parser design involves a lot of pattern matching and, in your case of a recursive parser, identification of expression nesting. Your BNF does not have to be formal or rigorous, as long as you think through how the parts will transition from one to the next.

Here is a BNF for your syntax:

number ::= an int or float numeric literal
identifier ::= an alphabetic character, followed by zero or more digits

factor_part ::= number | terms
factors ::= '(' factor_part (',' factor_part)... ')'

ident_factor ::= identifier '=' factors
terms ::= '[' ident_factor (',' ident_factor)... ']'

ident_term ::= identifier '=' terms
parser ::= '(' ident_term (',' ident_term)... ')'

BNF's usually start at the top and work down, I sometimes work backwards.

Here is the step-by-step conversion from BNF to Python pyparsing:

import pyparsing as pp

# we'll need a function analogous to the sum built-in for doing product
def product(values):
    ret = values[0]
    for v in values[1:]:
        ret *= v
    return ret

# symbols useful during parsing - suppress them from the output, since we
# just want the parsed and computed values
EQ, LPAR, RPAR, LBRACK, RBRACK = map(pp.Suppress, "=()[]")

# start converting the BNF to pyparsing expressions
# numeric literal from pyparsing's predefined exprs in pyparsing_common
# includes an attached parse action to convert parsed string to int or float,
# so we won't have to do that later
number = pp.pyparsing_common.number().setName("number")
ident = pp.Word(pp.alphas.upper(), pp.nums).setName("identifier")

# define placeholders for recursive elements
terms = pp.Forward().setName("terms")
factors = pp.Forward().setName("factors")

# insert grammar definitions for each - use '<<=' instead of '=' so we
# don't replace the Forward expressions, but insert the definitions into them
factor_part = number | terms
factors <<= LPAR + pp.delimitedList(factor_part) + RPAR

# when we evaluate factors, the identifiers are not used, we can suppress them
ident_factor = pp.Suppress(ident + EQ) + factors
terms <<= LBRACK + pp.delimitedList(ident_factor) + RBRACK

# add parse actions to do calculations - terms get added, factors get multiplied
terms.addParseAction(sum)
factors.addParseAction(product)

# finally define an overall expression for the sequence (P1=[...], P2=[...],
# etc.) and return as a dict
ident_term = ident + EQ + terms
parser = LPAR + pp.Dict(pp.delimitedList(pp.Group(ident_term))) + RPAR


# try parsing the given input string:
a = """(P1=[X=(0.6,3),Y=(0.4,-1)],
        P2=[X=(0.3,0),
            Y=(0.4,
               [Y1=(0.8,
                    [X=(0.2,1),Y=(0.8,2)]
                    ),
                Y2=(0.2,3)
               ]
               ),
            Z=(0.3,2)
            ],
        P3=[A=(1,1)])"""

try:
    print(parser.parseString(a).asDict())
except Exception as e:
    print(pp.ParseException.explain(e))

Gives:

{'P3': 1, 'P1': 1.4, 'P2': 1.416}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM