Expanding a logical statement (multiplying out)

此生再无相见时 提交于 2019-12-20 04:57:35

问题


I am looking for a way to expand a logical expression (in a string) of the form:

'(A or B) and ((C and D) or E)'

in Python to produce a list of all positive sets, i.e.

['A and C and D',
'A and E',
'B and C and D',
'B and E']

but I have been unable to find how to do this. I have investigated pyparser, but I cannot work out which example is relevant in this case. This may be very easy with some sort of logic manipulation but I do not know any formal logic. Any help, or a reference to a resource that might help would be greatly appreciated.


回答1:


Here's the pyparsing bit, taken from the example SimpleBool.py. First, use infixNotation (formerly known as operatorPrecedence) to define an expression grammar that supports parenthetical grouping, and recognizes precedence of operations:

from pyparsing import *

term = Word(alphas)

AND = Keyword("and")
OR = Keyword("or")

expr = infixNotation(term,
    [
    (AND, 2, opAssoc.LEFT),
    (OR, 2, opAssoc.LEFT),
    ])

sample = '(A or B) and ((C and D) or E)'

result = expr.parseString(sample)

from pprint import pprint
pprint(result.asList())

prints:

[[['A', 'or', 'B'], 'and', [['C', 'and', 'D'], 'or', 'E']]]

From this, we can see that the expression is at least parsed properly.

Next, we add parse actions to each level of the hierarchy of operations. For parse actions here, we actually pass classes, so that instead of executing functions and returning some value, the parser will call the class constructor and initializer and return a class instance for the particular subexpression:

class Operation(object):
    def __init__(self, tokens):
        self._tokens = tokens[0]
        self.assign()

    def assign(self):
        """
        function to copy tokens to object attributes
        """

    def __repr__(self):
        return self.__class__.__name__ + ":" + repr(self.__dict__)
    __str__ = __repr__

class BinOp(Operation):
    def assign(self):
        self.op = self._tokens[1]
        self.terms = self._tokens[0::2]
        del self._tokens

class AndOp(BinOp):
    pass

class OrOp(BinOp):
    pass

expr = infixNotation(term,
    [
    (AND, 2, opAssoc.LEFT, AndOp),
    (OR, 2, opAssoc.LEFT, OrOp),
    ])

sample = '(A or B) and ((C and D) or E)'

result = expr.parseString(sample)
pprint(result.asList())

returns:

[AndOp:{'terms': [OrOp:{'terms': ['A', 'B'], 'op': 'or'}, 
                   OrOp:{'terms': [AndOp:{'terms': ['C', 'D'], 
                                    'op': 'and'}, 'E'], 'op': 'or'}],
'op': 'and'}]

Now that the expression has been converted to a data structure of subexpressions, I leave it to you to do the work of adding methods to AndOp and OrOp to generate the various combinations of terms that will evaluate overall to True. (Look at the logic in the invregex.py example that inverts regular expressions for ideas on how to add generator functions to the parsed classes to generate the different combinations of terms that you want.)




回答2:


It sounds as if you want to convert these expressions to Disjunctive Normal Form. A canonical algorithm for doing that is the Quine-McCluskey algorithm; you can find some information about Python implementations thereof in the relevant Wikipedia article and in the answers to this SO question.



来源:https://stackoverflow.com/questions/17535438/expanding-a-logical-statement-multiplying-out

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!