statt " 1 and 0 or 1 " => 1 and 0 or 1

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
Pyreno
User
Beiträge: 1
Registriert: Sonntag 20. Januar 2019, 18:10

Hallo zusammen,

kann mir jemand hier irgendwie helfen:

ich werte Daten aus und bekomme zb. als Ergebnis einen String
code = ' ((1 or 0 or 0 and 1) or (0 or 0 and 1) and not 0)'
nun will als Ergebnis newcode = ((1 or 0 or 0 and 1) or (0 or 0 and 1) and not 0)
und am Ende True or False
ich wäre sehr dankbar wenn jemand eine Idee für mich hat
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Du sollst also als Hausaufgabe einen logischen Ausdruck parsen und auswerten. Was hast Du schon versucht?
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Spasseshalber mal mit `redbaron` gelöst, weil sich dessen Parser (fälschlicherweise) nicht an dem führenden Leerzeichen in `code` stört, im Gegensatz zum `ast.parser`, mit dem das natürlich ansonsten grundsätzlich analog funktionieren würde:

Code: Alles auswählen

#!/usr/bin/env python3
from functools import singledispatch
from operator import and_, or_

from redbaron import RedBaron
from redbaron.nodes import (
    AssociativeParenthesisNode,
    BooleanOperatorNode,
    IntNode,
    UnitaryOperatorNode,
)

NAME_TO_FUNCTION = {'and': and_, 'or': or_}


@singledispatch
def evaluate(node):
    raise ValueError(f'Unknown node type {type(node)}: {node.dumps()}')


@evaluate.register(AssociativeParenthesisNode)
def evaluate_parenthesis(node):
    return evaluate(node.value)


@evaluate.register(UnitaryOperatorNode)
def evaluate_unitary_operator(node):
    if node.value != 'not':
        raise ValueError(f'Unknown unitary operator {node.value!r}')
    else:
        return not evaluate(node.target)


@evaluate.register(BooleanOperatorNode)
def evaluate_boolean_operator(node):
    operator = NAME_TO_FUNCTION[node.value]
    return operator(evaluate(node.first), evaluate(node.second))


@evaluate.register(IntNode)
def evaluate_int(node):
    value = int(node.value)
    if value not in [0, 1]:
        raise ValueError(f'Unexcpected value {value} not 0 or 1')
    return bool(value)


def main():
    code = ' ((1 or 0 or 0 and 1) or (0 or 0 and 1) and not 0)'
    red = RedBaron(code)
    if not red:
        raise ValueError('No code found.')
    print(evaluate(red[0]))
    

if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wie schon geschrieben, mit `ast.parse()` geht das analog:

Code: Alles auswählen

#!/usr/bin/env python3
import ast
from ast import And, BoolOp, Expr, Not, Num, Or, UnaryOp
from functools import reduce, singledispatch
from operator import and_, or_

TYPE_TO_OPERATOR_FUNCTION = {And: and_, Or: or_}


@singledispatch
def evaluate(node):
    raise ValueError(f'Unknown node type {type(node)}: {ast.dump(node)}')


@evaluate.register(Expr)
def evaluate_expression(node):
    return evaluate(node.value)


@evaluate.register(UnaryOp)
def evaluate_not(node):
    if not isinstance(node.op, Not):
        raise ValueError(f'Unknown unitary operator {node.op}')
    else:
        return not evaluate(node.operand)


@evaluate.register(BoolOp)
def evaluate_boolean_operation(node):
    operator = TYPE_TO_OPERATOR_FUNCTION[type(node.op)]
    return reduce(operator, map(evaluate, node.values))


@evaluate.register(Num)
def evaluate_number(node):
    if node.n not in [0, 1]:
        raise ValueError(f'Unexcpected value {node.n} not 0 or 1')
    else:
        return bool(node.n)


def main():
    code = '((1 or 0 or 0 and 1) or (0 or 0 and 1) and not 0)'
    print(evaluate(ast.parse(code, mode='single').body[0]))
    

if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wenn man sich keinen fertigen Python-Parser hernehmen möchte, kann man sich natürlich auch selbst einen Parser schreiben. Zum Beispiel mit Pyparsing:

Code: Alles auswählen

#!/usr/bin/env python3
from functools import reduce
from operator import and_, or_

import pyparsing as pp


VALUE = pp.oneOf('0 1').setParseAction(lambda s, loc, toks: bool(int(toks[0])))
NOT, AND, OR = (pp.Suppress(s).setName(s) for s in ['not', 'and', 'or'])
EXPRESSION = pp.infixNotation(
    VALUE,
    [
        (NOT, 1, pp.opAssoc.RIGHT, lambda s, loc, toks: not toks[0][0]),
        (AND, 2, pp.opAssoc.LEFT, lambda s, loc, toks: reduce(and_, toks[0])),
        (OR, 2, pp.opAssoc.LEFT, lambda s, loc, toks: reduce(or_, toks[0])),
    ]
)
GRAMMAR = EXPRESSION + pp.lineEnd
GRAMMAR.enablePackrat()


def main():
    source = '((1 or 0 or 0 and 1) or (0 or 0 and 1) and not 0)'
    try:
        result = GRAMMAR.parseString(source)
    except pp.ParseException as error:
        print(error)
    else:
        print(result[0])


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten