简体   繁体   English

Python嵌套列表和递归问题

[英]Python nested lists and recursion problem

I posted this question under an alter yesterday not realising my account was still active after 9 months, sorry for the double post, i've fixed an error in my example pointed out by jellybean and i'll elaborate further on the context of the problem. 我昨天在一个变更下发布了这个问题,但我没有意识到我的帐户在9个月后仍处于激活状态,对于双重发布,我们感到抱歉,我已修复了豆形软糖指出的示例中的一个错误,我将在问题的背景下进行详细说明。

I'm trying to process a first order logic formula represented as nested lists and strings in python so that that its in disjunctive normal form, 我正在尝试处理表示为python中的嵌套列表和字符串的一阶逻辑公式,以使其处于析取范式,

ie

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

turns into 变成

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

where | 其中| is or and & is and . &

currently im using a recursive implementation which does several passes over a formula until it can't find any nested 'or' symbols inside a list argument for 'ands'. 目前,我正在使用递归实现,该实现对公式进行多次传递,直到无法在“ and”的列表参数中找到任何嵌套的“或”符号。 Its being used to process a set of nested formulas represented as strings and lists for universal computational tree logic so it will not only have | 它用于处理一组表示为字符串和通用计算树逻辑列表的嵌套公式,因此它将不仅具有| s and & s but temporal operators. s和& s,但临时运算符。

This is my implementation, performDNF(form) is the entry point. 这是我的实现, performDNF(form)是入口点。 Right now it performs a single pass over the formula with dnfDistributivity() which works for smaller inputs but when you use larger inputs the while loop checking function ( checkDistributivity() ) finds no | 现在,它使用dnfDistributivity()对公式执行一次传递,该公式适用于较小的输入,但是当您使用较大的输入时,while循环检查功能( checkDistributivity() )找不到| s inside of & s and terminates. & s并终止。 Help anyone, this is driving me mad. 帮助任何人,这使我发疯。

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

First, two general remarks about your code: 首先,关于您的代码的两个一般性评论:

  • Use return True instead of return 1 . 使用return True而不是return 1
  • Use isinstance(form, list) instead of isinstance(form, type([])) . 使用isinstance(form, list)代替isinstance(form, type([]))

Second, some other observations: 其次,其他一些观察:

  • I assume you also want to get rid of double negations. 我想您也想摆脱双重否定。 Currently your code doesn't do that. 目前,您的代码无法执行此操作。
  • Likewise, you'll need to apply one of DeMorgan's laws to push negations to the leaves. 同样,您需要应用DeMorgan的法律之一将否定性推到最后。

Aside from that, I think the readability of this code can be improved greatly. 除此之外,我认为该代码的可读性可以大大提高。 I'll give an alternative implementation which I believe to be correct. 我将给出一个我认为是正确的替代实现。 Let me know whether the code below works for you; 让我知道下面的代码是否适合您; I didn't go crazy with creating expressions, so I may have missed an edge case. 我并没有为创建表达式而疯狂,所以我可能错过了一个极端情况。 Lastly, I will focus only on the regular propositional connectives. 最后,我将只关注常规命题连接词。 It should be clear how to apply transformations involving CTL-specific connectives. 应该清楚如何应用涉及特定于CTL的连接词的转换。

  1. Create a class Op which represents an operator (connective): 创建一个表示运算符(连接)的类Op

     class Op(list): def __init__(self, *args): super().__init__(args) 

    The arguments to __init__ are the operands. __init__的参数是操作数。 This code uses super as defined in PEP 3135 and works only in Python 3.x In Python 2.x, you'll have to use super as defined in PEP 367 : 此代码使用PEP 3135中定义的super ,并且仅在Python 3.x中有效。在Python 2.x中,您将必须使用PEP 367中定义的super

     class Op(list): def __init__(self, *args): super(Op, self).__init__(args) 
  2. Create simple subclasses of Op for each operator. 为每个运算符创建Op简单子类。 For debugging purposes you may want to implement a custom __str__ method: 为了进行调试,您可能需要实现自定义__str__方法:

     class Neg(Op): def __str__(self): return '!(%s)' % tuple(self) class And(Op): def __str__(self): return '(%s) & (%s)' % tuple(self) class Or(Op): def __str__(self): return '(%s) | (%s)' % tuple(self) class AX(Op): def __str__(self): return 'AX (%s)' % tuple(self) ... 

    Now the formula !(a & b) can be created as Neg(And('a', 'b')) . 现在可以将公式!(a&b)创建为Neg(And('a', 'b'))

  3. Create very simple functions which apply a certain transformation once . 创建非常简单的函数,这些函数只需进行一次转换即可 This will keep the implementation clean. 这将使实现保持清洁。 Annotate these functions which some information on how they should be applied. 注释这些功能,其中包含有关应如何使用它们的一些信息。 A preorder function should be applied from top to bottom: first transform the root of the expression tree, then recurse. 应该从上到下应用一个预排序函数:首先转换表达式树的根,然后递归。 A postorder function should be applied to an expression after it has been recursively applied to subexpressions. 后置函数递归应用于子表达式后,应将其应用于表达式。 Use isinstance to check the type of connectives. 使用isinstance检查isinstance的类型。

    1. We start easy: the function removeDoubleNeg removes double negations: 我们从简单开始:函数removeDoubleNeg删除双重否定:

       @expressionTransformation('postorder') def removeDoubleNeg(expr): if isinstance(expr, Neg) and isinstance(expr[0], Neg): return expr[0][0] 
    2. Next, let's define one of DeMorgan's laws: 接下来,让我们定义DeMorgan的定律之一:

       @expressionTransformation('preorder') def deMorgan(expr): if isinstance(expr, Neg) and isinstance(expr[0], And): return Or(Neg(expr[0][0]), Neg(expr[0][1])) 
    3. And now the function which this question is all about: 现在,这个问题所涉及的功能是:

       @expressionTransformation('preorder', 'postorder') def distribute(expr): if isinstance(expr, And): if isinstance(expr[0], Or): return Or(And(expr[0][0], expr[1]), And(expr[0][1], expr[1])) if isinstance(expr[1], Or): return Or(And(expr[0], expr[1][0]), And(expr[0], expr[1][1])) 

      Wow! 哇! That's a lot less code! 更少的代码!

  4. Okay, so how does this work? 好的,这如何工作? Observe that any naive implementation of an expression transformation f will involve boilerplate code: 观察到表达式转换f的任何简单实现都会涉及样板代码:

    1. Test whether the argument is a connective (as opposed to a constant or variable). 测试参数是否是连接词(与常量或变量相对)。
    2. Attempt to apply f to the root of the expression tree. 尝试将f应用于表达式树的根。
    3. Recurse. 递归。
    4. Return the result. 返回结果。

    Depending on f , step 1 and 2 may need to be reversed ( postorder instead of preorder ). 根据f ,步骤1和步骤2可能需要颠倒( 后置而不是预购 )。 Still, every implementation of f will look alike. 尽管如此, f的每个实现看起来都是相似的。 You will want to avoid boilerplate code, especially if you plan to define many more transformations. 您将要避免样板代码,特别是如果您计划定义更多的转换。 It is the lack of this boilerplate that made the functions defined in the previous step so concise (and thus easy to debug!). 缺少此样板可简化上一步中定义的功能(因此易于调试!)。 The decorators returned by the function expressionTransformation solved this problem. 函数expressionTransformation返回的装饰器解决了此问题。 Its implementation is as follows: 其实现如下:

     from functools import wraps def expressionTransformation(*args): def wrap(f): @wraps(f) def recursiveTransformation(expr): if not isinstance(expr, Op): return expr if 'postorder' in args: expr[:] = map(recursiveTransformation, expr) res = f(expr) expr = expr if res is None else res if 'preorder' in args: expr[:] = map(recursiveTransformation, expr) return expr return recursiveTransformation return wrap 

    What happens here is the following: 以下是发生的情况:

    1. The function expressionTransformation returns a decorator (named wrap ) which receives the transformation function f as its argument. 函数expressionTransformation返回一个装饰器(名为wrap ),该装饰器接收转换函数f作为其参数。
    2. wrap returns a recursive function recursiveTransformation which applies f to its argument expr only if this argument is a connective. wrap返回一个递归函数recursiveTransformation ,仅当此参数为expr才将f应用于其参数expr
    3. Depending on the arguments args supplied to expressionTransformation , f will be applied before or after (or before and after) applying f to the subexpressions. 取决于参数args供给到expressionTransformationf之前之后 (或之前之后)将F到子表达式来施加。
    4. The assumption is made that f may return None if no transformation is made. 假设如果不进行任何转换,则f可能返回None

    The function functools.wraps is used to copy certain properties of f , such as its name, to recursiveTransformation . 函数functools.wraps用于将f某些属性(例如其名称)复制到recursiveTransformation This functionality is non-essential. 此功能不是必需的。

    (Note that there are more efficient way to create preorder and postorder transformations than using the tests 'postorder' in args and 'preorder' in args over and over, but I chose this for clarity.) (请注意,与一遍又一遍地'preorder' in args使用测试'postorder' in args'postorder' in args 'preorder' in args相比,创建预序和后序转换的方法更为有效,但为清楚起见,我选择了此方法。)

  5. That's all. 就这样。 We can now easily combine these functions (note that this function should not be decorated): 现在,我们可以轻松地组合这些功能(请注意,不应修饰此功能):

     def toDNF(expr): return distribute(removeDoubleNeg(deMorgan(expr))) 
  6. You can test the code with statements like these: 您可以使用以下语句测试代码:

     toDNF(AX(And(Or('a', 'b'), And(Or('c', 'd'), Or('e', 'f'))))) toDNF(Neg(And(Or(Neg(Neg('a')), 'b'), And(Or('c', 'd'), Or('e', 'f'))))) 

You have: 你有:

    elif len(form) == 2:
        result = self.checkDistributivity(form[1], result)
        print result

Shouldn't that be: 那不应该是:

    elif len(form) == 2:
        result_1 = self.checkDistributivity(form[1], result)
        result_2 = self.checkDistributivity(form[2], result) 
        if result_1 or result_2:
            return 1

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

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