Python嵌套列表和递归问题

时间:2009-11-24 23:13:44

标签: python recursion boolean-logic

我昨天在一个修改后发布了这个问题没有意识到我的帐户在9个月后仍然有效,对于双重帖子感到抱歉,我已经修复了我在软糖指出的例子中的错误,我将进一步阐述上下文问题。

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

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

变成

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

其中| &

目前我正在使用递归实现,它对公式进行多次传递,直到找不到'ands'的列表参数内的任何嵌套'或'符号。它用于处理一组嵌套公式,表示为通用computational tree logic的字符串和列表,因此它不仅具有|& s,而且还包含时间运算符。

这是我的实现,performDNF(form)是切入点。现在,它使用dnfDistributivity()对公式执行单次传递,适用于较小的输入,但是当您使用较大的输入时,while循环检查函数(checkDistributivity())在|内找不到& {1}}并终止。帮助任何人,这让我很生气。

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

2 个答案:

答案 0 :(得分:3)

首先,关于您的代码的两个一般性评论:

  • 使用return True代替return 1
  • 使用isinstance(form, list)代替isinstance(form, type([]))

其次,其他一些观察结果:

  • 我假设你也想摆脱双重否定。目前,您的代码不会这样做。
  • 同样,你需要应用DeMorgan的一条法律来推迟否定。

除此之外,我认为这段代码的可读性可以大大提高。我会给出一个我认为正确的替代实现。让我知道下面的代码是否适合您;我没有为创建表达式而疯狂,所以我可能错过了一个边缘案例。最后,我将只关注常规命题连接词。应该清楚如何应用涉及CTL特异性结缔组织的转化。

  1. 创建一个代表运算符(连接词)的类Op

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

    __init__的参数是操作数。此代码使用super中定义的PEP 3135,仅适用于Python 3.x在Python 2.x中,您必须使用super中定义的PEP 367

    class Op(list):
        def __init__(self, *args):
            super(Op, self).__init__(args)
    
  2. 为每个运算符创建Op的简单子类。出于调试目的,您可能希望实现自定义__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)
    ...
    

    现在公式!(a& b)可以创建为Neg(And('a', 'b'))

  3. 创建非常简单的函数,这些函数应用一次。这将使实施保持清洁。 Annotate这些函数提供了有关如何应用它们的一些信息。应该从上到下应用预订函数:首先转换表达式树的根,然后递归。在递归地将表达式应用于子表达式之后,应将 postorder 函数应用于表达式。使用isinstance检查连接词的类型。

    1. 我们从容易开始:函数removeDoubleNeg删除了双重否定:

      @expressionTransformation('postorder')
      def removeDoubleNeg(expr):
          if isinstance(expr, Neg) and isinstance(expr[0], Neg):
              return expr[0][0]
      
    2. 接下来,我们来定义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. 现在这个问题的功能就是:

      @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]))
      

      哇!这是更少的代码!

  4. 好的,这是怎么回事?观察表达式转换 f 的任何天真实现都将涉及样板代码:

    1. 测试参数是否是连接词(与常量或变量相对)。
    2. 尝试将 f 应用于表达式树的根目录。
    3. 递归。
    4. 返回结果。
    5. 根据 f ,可能需要撤消步骤1和2( postorder 而不是预订)。不过, f 的每个实现看起来都很相似。您将需要避免使用样板代码,尤其是在您计划定义更多转换时。缺少这种样板,使前一步骤中定义的功能变得简洁(因此易于调试!)。函数expressionTransformation返回的装饰器解决了这个问题。其实施如下:

      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
      

      这里会发生以下情况:

      1. 函数expressionTransformation返回一个装饰器(名为wrap),它接收转换函数f作为其参数。
      2. wrap返回一个递归函数recursiveTransformation,只有当这个参数是连接词时才会将f应用于它的参数expr
      3. 根据提供给args的{​​{1}}参数,{<1}}将在之前之后之前应用 >和之后将 f 应用于子表达式。
      4. 如果没有进行转换,则假设expressionTransformation可能会返回f
      5. 函数functools.wraps用于将f的某些属性(例如其名称)复制到None。此功能不是必需的。

        (请注意,创建前序和后序转换比使用测试frecursiveTransformation反复进行更有效的方法,但为了清晰起见,我选择了这一点。)

      6. 这就是全部。我们现在可以轻松地组合这些功能(请注意,此功能不应该被装饰):

        'postorder' in args
      7. 您可以使用以下语句测试代码:

        'preorder' in args

答案 1 :(得分:0)

你有:

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

不应该是:

    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