在 Python 中尽可能在逻辑公式中放置括号

问题描述 投票:0回答:1

如何在 Python 的一阶逻辑公式中尽可能放置括号?这个公式只存在于逻辑非 ¬ 和 ∧ 或 ∨,意味着 → 和 bi 意味着 ↔。例如“¬(A ∧ B) ∨ C”产生 ((¬(A ∧ B)) ∨ C)。你可以在下面看到我的尝试。我想要一个将每个运算符包装在括号中的函数(请参阅测试),因此括号内只有一个运算符。有谁知道为什么我的代码失败或解决这个问题的任何方法?

import re

def add_brackets(formula):
    # Define operator priorities
    priority = {'¬': 4, '∧': 3, '∨': 2, '→': 1, '↔': 0}

    # Convert formula to list of tokens
    tokens = re.findall(r'\(|\)|¬|∧|∨|→|↔|[A-Z]', formula)

    # Initialize stack to hold operators
    stack = []

    # Initialize queue to hold output
    output = []

    # Loop through tokens
    for token in tokens:
        if token.isalpha():
            output.append(token)
        elif token == '¬':
            stack.append(token)
        elif token in '∧∨→↔':
            while stack and stack[-1] != '(' and priority[token] <= priority[stack[-1]]:
                output.append(stack.pop())
            stack.append(token)
        elif token == '(':
            stack.append(token)
        elif token == ')':
            while stack and stack[-1] != '(':
                output.append(stack.pop())
            stack.pop()
    
    # Empty out remaining operators on the stack
    while len(stack) != 0:
        if stack[-1] == '(':
            raise ValueError('Unmatched left parenthesis')
        output.append(stack.pop())

    # Loop through output
    for i, token in enumerate(output):
        if token in '∧∨→↔':
            if len(output) < i + 3:
                raise ValueError('Invalid formula')
            result = '({} {} {})'.format(output[i+1], token, output[i+2])
            output[i:i+3] = [result]
    
    # Return final result
    return output[0]


# Formula 1
formula1 = "A ∧ B ∨ C"
assert add_brackets(formula1) == "(A ∧ (B ∨ C))"

# Formula 2
formula2 = "¬(A ∧ B) ∨ C"
assert add_brackets(formula2) == "((¬(A ∧ B)) ∨ C)"

# Formula 3
formula3 = "A ∧ B ∧ C ∧ D ∧ E"
assert add_brackets(formula3) == "((((A ∧ B) ∧ C) ∧ D) ∧ E))"

# Formula 4
formula4 = "¬A ∧ ¬B ∧ ¬C"
assert add_brackets(formula4) == "(((¬A) ∧ (¬B)) ∧ (¬C))"

# Formula 5
formula5 = "A ∧ ¬(B ∨ C)"
assert add_brackets(formula5) == "(A ∧ (¬(B ∨ C)))"

# Formula 6
formula6 = "A ∨ B → C ∧ D"
assert add_brackets(formula6) == "((A ∨ B) → (C ∧ D))"

# Formula 7
formula7 = "A ∧ B → C ∨ D"
assert add_brackets(formula7) == "(((A ∧ B) → (C ∨ D)))"

# Formula 8
formula8 = "¬(A ∧ B) → C ∨ D"
assert add_brackets(formula8) == "((¬(A ∧ B)) → (C ∨ D))"

# Formula 9
formula9 = "(A → B) → (C → D)"
assert add_brackets(formula9) == "((A → B) → (C → D))"

# Formula 10
formula10 = "(A ∧ B) ∨ (C ∧ D) → E"
assert add_brackets(formula10) == "(((A ∧ B) ∨ (C ∧ D)) → E)"
python string parsing logic
1个回答
0
投票

Pyparsing 是一个 Python 解析器组合器库,用于使用其自己的嵌入式 DSL 语法构建解析器。将其视为一种冗长的正则表达式,但具有更多递归语法、命名结果和解析时回调的功能。

Pyparsing 还包括用于常见构造的帮助程序或宏方法,如分隔列表、带引号的字符串和其他模式,如 IP 地址、日期/时间时间戳等。一种这样的方法是用于定义中缀表示法的方法,你可以在其中提供和表达对于基本操作数(在您的情况下,单个字母)和按优先顺序定义操作的元组列表。每个元组包含符号、操作数的数量以及右或左结合性。

这是解析语法的整个 pyparsing 解析器:

import pyparsing as pp

var = pp.Word(pp.alphas)
expr = pp.infix_notation(
    var,
    [
        # '¬': 4, '∧': 3, '∨': 2, '→': 1, '↔': 0
        ('¬', 1, pp.opAssoc.RIGHT),
        ('∧', 2, pp.opAssoc.LEFT),
        ('∨', 2, pp.opAssoc.LEFT),
        ('→', 2, pp.opAssoc.LEFT),
        ('↔', 2, pp.opAssoc.LEFT),
    ]
)

Pyparsing 表达式有一个

run_tests
方法来提供测试字符串列表,
run_tests
将解析每个字符串并将解析结果列为列表。

这是您的测试字符串:

test_strings = (
    """
    # Formula 1
    A ∧ B ∨ C

    # Formula 2
    ¬(A ∧ B) ∨ C

    # Formula 3
    A ∧ B ∧ C ∧ D ∧ E

    # Formula 4
    ¬A ∧ ¬B ∧ ¬C

    # Formula 5
    A ∧ ¬(B ∨ C)

    # Formula 6
    A ∨ B → C ∧ D

    # Formula 7
    A ∧ B → C ∨ D

    # Formula 8
    ¬(A ∧ B) → C ∨ D

    # Formula 9
    (A → B) → (C → D)

    # Formula 10
    (A ∧ B) ∨ (C ∧ D) → E
    """
)

expr.run_tests(test_strings, full_dump=False)

调用

run_tests
给出这些结果:

# Formula 1
A ∧ B ∨ C
[[['A', '∧', 'B'], '∨', 'C']]

# Formula 2
¬(A ∧ B) ∨ C
[[['¬', ['A', '∧', 'B']], '∨', 'C']]

# Formula 3
A ∧ B ∧ C ∧ D ∧ E
[['A', '∧', 'B', '∧', 'C', '∧', 'D', '∧', 'E']]

# Formula 4
¬A ∧ ¬B ∧ ¬C
[[['¬', 'A'], '∧', ['¬', 'B'], '∧', ['¬', 'C']]]

# Formula 5
A ∧ ¬(B ∨ C)
[['A', '∧', ['¬', ['B', '∨', 'C']]]]

# Formula 6
A ∨ B → C ∧ D
[[['A', '∨', 'B'], '→', ['C', '∧', 'D']]]

# Formula 7
A ∧ B → C ∨ D
[[['A', '∧', 'B'], '→', ['C', '∨', 'D']]]

# Formula 8
¬(A ∧ B) → C ∨ D
[[['¬', ['A', '∧', 'B']], '→', ['C', '∨', 'D']]]

# Formula 9
(A → B) → (C → D)
[[['A', '→', 'B'], '→', ['C', '→', 'D']]]

# Formula 10
(A ∧ B) ∨ (C ∧ D) → E
[[[['A', '∧', 'B'], '∨', ['C', '∧', 'D']], '→', 'E']]

这非常接近您想要的输出,尽管这些是经过解析的嵌套列表,我们真的希望您的输出字符串带有括号。您还会注意到 pyparsing 不会发出所有二进制输出,但是对于具有相同运算符的重复操作数,您只会得到一个列表(就像公式 3

A ∧ B ∧ C ∧ D ∧ E
->
[['A', '∧', 'B', '∧', 'C', '∧', 'D', '∧', 'E']]
的列表)。我们可以为这些列表编写一个递归重组器(我鼓励你自己尝试编写),或者我们可以在解析时使用回调对每个解析组进行重组,在 pyparsing 中我们称之为 parse action
infix_notation
的元组可以采用用作解析操作的第四个元素。这是修改后的语法,具有一元和二元运算符的解析操作:

def unary_regroup(tokens):
    t = tokens[0]
    return [pp.ParseResults([t[0], [t[1:]]])]

def binary_regroup(tokens):
    t = t[0]
    op = t[1]
    opers = t[::2]
    ret = pp.ParseResults([opers[0], op, opers[1]])
    for operand in opers[2:]:
        ret = pp.ParseResults([ret, op, operand])
    return [ret]

var = pp.Word(pp.alphas)
expr = pp.infix_notation(
    var,
    [
        # '¬': 4, '∧': 3, '∨': 2, '→': 1, '↔': 0
        ('¬', 1, pp.opAssoc.RIGHT, unary_regroup),
        ('∧', 2, pp.opAssoc.LEFT, binary_regroup),
        ('∨', 2, pp.opAssoc.LEFT, binary_regroup),
        ('→', 2, pp.opAssoc.LEFT, binary_regroup),
        ('↔', 2, pp.opAssoc.LEFT, binary_regroup),
    ]
)

(我为这两个函数的复杂性道歉,当解析操作需要返回不是简单结构的标记列表时,pyparsing 有点挑剔。)

如果我们现在为公式 3 调用

run_tests
,您可以看到之前的一体机组已分解为嵌套的二进制组:

# Formula 3
A ∧ B ∧ C ∧ D ∧ E
[[[[['A', '∧', 'B'], '∧', 'C'], '∧', 'D'], '∧', 'E']]

现在我们可以编写递归方法将其转换为带有嵌套括号的字符串:

for test in test_strings.splitlines():
    test = test.strip()
    if not test or test.startswith("#"):
        print(test)
        continue
    parsed = expr.parse_string(test)

    def enclose_groups_in_parens(t):
        ret = []
        for ob in t:
            if isinstance(ob, (pp.ParseResults, list)):
                ret.append(enclose_groups_in_parens(ob))
            else:
                ret.append(ob)
        return f"({' '.join(ret)})" if len(ret) > 1 else ' '.join(ret)

    regrouped_string = enclose_groups_in_parens(parsed)
    print(test, '->', regrouped_string)

打印:

# Formula 1
A ∧ B ∨ C -> ((A ∧ B) ∨ C)

# Formula 2
¬(A ∧ B) ∨ C -> ((¬ (A ∧ B)) ∨ C)

# Formula 3
A ∧ B ∧ C ∧ D ∧ E -> ((((A ∧ B) ∧ C) ∧ D) ∧ E)

# Formula 4
¬A ∧ ¬B ∧ ¬C -> (((¬ A) ∧ (¬ B)) ∧ (¬ C))

# Formula 5
A ∧ ¬(B ∨ C) -> (A ∧ (¬ (B ∨ C)))

# Formula 6
A ∨ B → C ∧ D -> ((A ∨ B) → (C ∧ D))

# Formula 7
A ∧ B → C ∨ D -> ((A ∧ B) → (C ∨ D))

# Formula 8
¬(A ∧ B) → C ∨ D -> ((¬ (A ∧ B)) → (C ∨ D))

# Formula 9
(A → B) → (C → D) -> ((A → B) → (C → D))

# Formula 10
(A ∧ B) ∨ (C ∧ D) → E -> (((A ∧ B) ∨ (C ∧ D)) → E)

© www.soinside.com 2019 - 2024. All rights reserved.