繁体   English   中英

Python嵌套列表和递归问题

[英]Python nested lists and recursion problem

我正在尝试处理表示为python中的嵌套列表和字符串的一阶逻辑公式,以使其处于析取范式,

即['&',['|','a','b'],['|','c','d']]

变成

['|' ['&',['&','a','c'],['&','b','c']],['&',['&','a','d '],['&','b','d']]]

在哪里 是“或”且&是“与”。

目前,我正在使用递归实现,该实现对公式进行多次传递,直到无法在“ and”的列表参数中找到任何嵌套的“或”符号。

这是我的实现,performDNF(form)是入口点。 现在,它对公式执行了一次遍历,但是while循环检查功能在'&'内找不到'|'并终止,请帮助任何使我发狂的人。

def dnfDistributivity(self, form):
    if isinstance(form, type([])):
        if len(form) == 3:
            if form[0] == '&':
                if form[1][0] == '|':
                    form = ['|', ['&', form[2], form[1][1]], ['&', form[2], form[1][2]]]
                elif form[2][0] == '|':
                    form = ['|', ['&', form[1], form[2][1]], ['&', form[1], form[2][2]]]
            form[1] = self.dnfDistributivity(form[1])
            form[2] = self.dnfDistributivity(form[2])
        elif len(form) == 2:
            form[1] = self.dnfDistributivity(form[1])
    return form

def checkDistributivity(self, form, result = 0):
    if isinstance(form, type([])):
        if len(form) == 3:
            if form[0] == '&':
                print "found &"
                if isinstance(form[1], type([])):
                    if form[1][0] == '|':
                        return 1
                elif isinstance(form[2], type([])):
                    if form[2][0] == '|':
                        return 1
                else:
                    result = self.checkDistributivity(form[1], result)
                    print result
                    if result != 1:
                        result = self.checkDistributivity(form[2], result)
                        print result
        elif len(form) == 2:
            result = self.checkDistributivity(form[1], result)
            print result
    return result

def performDNF(self, form):
    while self.checkDistributivity(form):
        form = self.dnfDistributivity(self.dnfDistributivity(form))
    return form

好的,这是一个可行的实际解决方案。

我不理解您的代码,也没有听说过DNF,因此我首先研究问题。

DNF上Wikipedia页面非常有帮助。 它包括描述DNF的语法。

基于此,我编写了一组简单的递归函数,我相信它们可以正确识别您需要的格式的DNF。 我的代码包括一些简单的测试用例。

然后,我意识到数据表示形式的二叉树性质使得通过编写递归求反的函数negate()来应用DeMorgan的定律来简化“非”情况相对简单,其余的就位。

我已经包含了测试用例。 它似乎有效。

我没有进一步的计划。 如果有人发现错误,请提供一个测试用例,我将进行研究。

此代码应在任何2.4版或更高版本的Python上运行。 您甚至可以通过使用简单的字符列表替换Frozenset来将其移植到旧版Python。 我使用Python 3.x进行了测试,发现异常语法已更改,因此如果要在Python 3下运行它,则需要更改raise行; 重要部分都起作用。

在这个问题,你没有提到你用什么字符not ; 给您的使用&and| 对于or ,我认为您可能会使用! not并相应地编写代码。 这是一份让我为难,你的代码的事情之一:永远也不要指望找到not在你的输入?

我在这方面工作很有趣。 它不像数独游戏那样毫无意义。

import sys


ch_and = '&'
ch_not = '!'
ch_or = '|'

def echo(*args):
    # like print() in Python 3 but works in 2.x or in 3
    sys.stdout.write(" ".join(str(x) for x in args) + "\n")

try:
    symbols = frozenset([ch_and, ch_not, ch_or])
except NameError:
    raise Exception, "sorry, your Python is too old for this code"

try:
    __str_type = basestring
except NameError:
    __str_type = str


def is_symbol(x):
    if not isinstance(x, __str_type) or len(x) == 0:
        return False
    return x[0] in symbols

def is_and(x):
    if not isinstance(x, __str_type) or len(x) == 0:
        return False
    return x[0] == ch_and

def is_or(x):
    if not isinstance(x, __str_type) or len(x) == 0:
        return False
    return x[0] == ch_or

def is_not(x):
    if not isinstance(x, __str_type) or len(x) == 0:
        return False
    return x[0] == ch_not

def is_literal_char(x):
    if not isinstance(x, __str_type) or len(x) == 0:
        return False
    return x[0] not in symbols

def is_list(x, n):
    return isinstance(x, list) and len(x) == n


def is_literal(x):
    """\
True if x is a literal char, or a 'not' followed by a literal char."""
    if is_literal_char(x):
        return True
    return is_list(x, 2) and is_not(x[0]) and is_literal_char(x[1])


def is_conjunct(x):
    """\
True if x is a literal, or 'and' followed by two conjuctions."""
    if is_literal(x):
        return True
    return (is_list(x, 3) and
            is_and(x[0]) and is_conjunct(x[1]) and is_conjunct(x[2]))


def is_disjunct(x):
    """\
True if x is a conjunction, or 'or' followed by two disjuctions."""
    if is_conjunct(x):
        return True
    return (is_list(x, 3) and
            is_or(x[0]) and is_disjunct(x[1]) and is_disjunct(x[2]))



def is_dnf(x):
    return is_disjunct(x)

def is_wf(x):
    """returns True if x is a well-formed list"""
    if is_literal(x):
        return True
    elif not isinstance(x, list):
        raise TypeError, "only lists allowed"
    elif len(x) == 2 and is_not(x[0]) and is_wf(x[1]):
        return True
    else:
        return (is_list(x, 3) and (is_and(x[0]) or is_or(x[0])) and
                is_wf(x[1]) and is_wf(x[2]))

def negate(x):
    # trivial: negate a returns !a
    if is_literal_char(x):
        return [ch_not, x]

    # trivial: negate !a returns a
    if is_list(x, 2) and is_not(x[0]):
        return x[1]

    # DeMorgan's law:  negate (a && b) returns (!a || !b)
    if is_list(x, 3) and is_and(x[0]):
        return [ch_or, negate(x[1]), negate(x[2])]

    # DeMorgan's law:  negate (a || b) returns (!a && !b)
    if is_list(x, 3) and is_or(x[0]):
        return [ch_and, negate(x[1]), negate(x[2])]

    raise ValueError, "negate() only works on well-formed values."

def __rewrite(x):
    # handle all dnf, which includes simple literals.
    if is_dnf(x):
        # basis case. no work to do, return unchanged.
        return x

    if len(x) == 2 and is_not(x[0]):
        x1 = x[1]
        if is_list(x1, 2) and is_not(x1[0]):
            # double negative!  throw away the 'not' 'not' and keep rewriting.
            return __rewrite(x1[1])
        assert is_list(x1, 3)
        # handle non-inner 'not'
        return __rewrite(negate(x1))

    # handle 'and' with 'or' inside it
    assert is_list(x, 3) and is_and(x[0]) or is_or(x[0])
    if len(x) == 3 and is_and(x[0]):
        x1, x2 = x[1], x[2]
        if ((is_list(x1, 3) and is_or(x1[0])) and
                (is_list(x2, 3) and is_or(x2[0]))):
# (a || b) && (c || d) -- (a && c) || (b && c) || (a && d) || (b && d)
            lst_ac = [ch_and, x1[1], x2[1]]
            lst_bc = [ch_and, x1[2], x2[1]]
            lst_ad = [ch_and, x1[1], x2[2]]
            lst_bd = [ch_and, x1[2], x2[2]]
            new_x = [ch_or, [ch_or, lst_ac, lst_bc], [ch_or, lst_ad, lst_bd]]
            return __rewrite(new_x)

        if (is_list(x2, 3) and is_or(x2[0])):
# a && (b || c)  -- (a && b) || (a && c)
            lst_ab = [ch_and, x1, x2[1]]
            lst_ac = [ch_and, x1, x2[2]]
            new_x = [ch_or, lst_ab, lst_ac]
            return __rewrite(new_x)

        if (is_list(x1, 3) and is_or(x1[0])):
# (a || b) && c  -- (a && c) || (b && c)
            lst_ac = [ch_and, x1[1], x2]
            lst_bc = [ch_and, x1[2], x2]
            new_x = [ch_or, lst_ac, lst_bc]
            return __rewrite(new_x)

    return [x[0], __rewrite(x[1]), __rewrite(x[2])]
    #return x

def rewrite(x):
    if not is_wf(x):
        raise ValueError, "can only rewrite well-formed lists"
    while not is_dnf(x):
        x = __rewrite(x)
    return x


#### self-test code ####

__failed = False
__verbose = True
def test_not_wf(x):
    global __failed
    if is_wf(x):
        echo("is_wf() returned True for:", x)
        __failed = True

def test_dnf(x):
    global __failed
    if not is_wf(x):
        echo("is_wf() returned False for:", x)
        __failed = True
    elif not is_dnf(x):
        echo("is_dnf() returned False for:", x)
        __failed = True

def test_not_dnf(x):
    global __failed
    if not is_wf(x):
        echo("is_wf() returned False for:", x)
        __failed = True
    elif is_dnf(x):
        echo("is_dnf() returned True for:", x)
        __failed = True
    else:
        xr = rewrite(x)
        if not is_wf(xr):
            echo("rewrite produced non-well-formed for:", x)
            echo("result was:", xr)
            __failed = True
        elif not is_dnf(xr):
            echo("rewrite failed for:", x)
            echo("result was:", xr)
            __failed = True
        else:
            if __verbose:
                echo("original:", x)
                echo("rewritten:", xr)
                echo()

def self_test():
    a, b, c, d = 'a', 'b', 'c', 'd'
    test_dnf(a)
    test_dnf(b)
    test_dnf(c)
    test_dnf(d)

    lstna = [ch_not, a]
    test_dnf(lstna)

    lstnb = [ch_not, b]
    test_dnf(lstnb)

    lsta = [ch_and, a, b]
    test_dnf(lsta)

    lsto = [ch_or, a, b]
    test_dnf(lsto)

    test_dnf([ch_and, lsta, lsta])

    test_dnf([ch_or, lsta, lsta])

    lstnn = [ch_not, [ch_not, a]]
    test_not_dnf(lstnn)

    test_not_dnf([ch_and, lstnn, lstnn])

    # test 'and'/'or' inside 'not'
    test_not_dnf([ch_not, lsta])
    test_not_dnf([ch_not, lsto])

    # test 'or' inside of 'and'
    # a&(b|c) --> (a&b)|(b&c)
    test_not_dnf([ch_and, a, [ch_or, b, c]])
    # (a|b)&c --> (a&c)|(b&c)
    test_not_dnf([ch_and, [ch_or, a, b], c])
    # (a|b)&(c|d) --> ((a&c)|(b&c))|((a&d)|(b&d))
    test_not_dnf([ch_and, [ch_or, a, b], [ch_or, c, d]])

    # a&a&a&(b|c) --> a&a&(a&b|b&c) --> a&(a&a&b|a&b&c) --> (a&a&a&b|a&a&b&c)
    test_not_dnf([ch_and, a, [ch_and, a, [ch_and, a, [ch_or, b, c]]]])

    if __failed:
        echo("one or more tests failed")

self_test()

现在,我很抱歉地说这句话,但是我对它的思考越多,我想你就越可能会让我为你做功课。 因此,我只是编写了此代码的改进版本,但我不打算在这里共享它。 我将其作为练习留给您。 在描述完之后,您应该可以轻松地做到这一点。

我有一个while循环重复调用__rewrite()是一个可怕的技巧。 rewrite()函数应该能够通过一次调用__rewrite()来重写树结构。 只需进行一些简单的更改,您就可以摆脱while循环; 我做到了,并对其进行了测试,并且有效。 您希望__rewrite()沿着树走下去,然后在备份的途中重写内容, __rewrite()工作。 您还可以修改__rewrite()以在列表格式不正确时返回错误,并摆脱对is_wf()的调用; 这也很容易。

我怀疑您的老师会在while循环中停靠您的点,因此您应该有动力尝试一下。 希望您乐在其中,也希望您从我的代码中学到了有用的东西。

抱歉,我并不是真的想了解您的解决方案。 我认为这太难读了,而且可能太麻烦了。

如果您有DNF,您要做的就是找到原子的所有组合,并从每个子列表中取出一个。 这几乎可以归结为这个问题……从每个“或”子句中,您都需要一个原子,并将所有与AND结合在一起。

所有这些可能的AND子句的OR组合都可产生您想要的结果。 对?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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