在Python中重置装饰器以获取递归函数

时间:2015-12-29 03:20:54

标签: python recursion python-decorators

我正在使用递归函数来解决使用完整KK算法的分区问题。

我的算法搜索可能分区的树。我希望通过阻止特定的树枝来修剪那棵树。我的“修剪器”想跟踪最小结果(事实上到目前为止发现的两个子集之和之间的最小差异)。

我的代码格式为:

def prune(branch):

    def pruner(list_):
         # If only single element in list, reached end of branch
         if len(list_) == 1:
             return list_[0]

         # ... pruning code here ... 
         # Decision to prune depends on pruner.min

         # If didn't prune, calculate as usual
         # tracking minimum
         min_branch = branch(list_)
         pruner.min = min(pruner.min, min_branch)
         return min_branch

     pruner.__name__ = branch.__name__
     pruner.min = float("inf")
     return pruner


 @prune
 def branch(list_):
     # ... code to make two new branches here ...
     return min(branch(list_1), branch(list_2))

它适用于单个branch的调用 - pruner.min在我的装饰器的开头设置一次 - 而branch会返回正确答案

问题是如果我连续两次或更多次使用该功能。在这种情况下,每次调用后都不会重置pruner.min,导致我决定修剪分支时出现问题。

对于pruner.min = float("inf")函数的每次初始(即非递归)调用,我如何(优雅地)重置branch?我唯一能想到的是添加一个first关键字参数def branch(list_, first=True),我将其设置为False,以便在branch函数内进行递归调用。我的装饰师可以看看这个论点。这是最好的方式吗?

为了完整性,这是一个可运行的示例,可以得到正确的答案。欢迎任何更一般的评论。

import copy

def prune(CKK_branch):
    def pruner(list_):

        internal_list = copy.deepcopy(list_)

        # Check whether end of branch or whether optimal solution achieved
        if pruner.min == 0.:
            return pruner.min
        elif len(internal_list) == 1:
            return list_[0]

        # Sort branch
        internal_list.sort(reverse=True)

        # Prune branch
        if pruner.min < internal_list[0] - sum(internal_list[1:]):
            return internal_list[0]

        # Find minimum in branch and track minimum of all branches
        min_branch = CKK_branch(internal_list)
        pruner.min = min(pruner.min, min_branch)

        return min_branch

    pruner.__name__ = CKK_branch.__name__
    pruner.min = float("inf")
    return pruner


@prune
def CKK(list_):
    internal_list = copy.deepcopy(list_)
    # Replace maximum two numbers by their difference and their sum in two
    # branches
    diff = internal_list[0] - internal_list[1]
    sum_ = internal_list[0] + internal_list[1]
    sum_tree = copy.deepcopy(internal_list)
    diff_tree = copy.deepcopy(internal_list)
    del sum_tree[0:2]
    del diff_tree[0:2]
    sum_tree.append(sum_)
    diff_tree.append(diff)

    return min(CKK(diff_tree), CKK(sum_tree)) 

example_list_1 = [1.4,
                  10.1,
                  19.55,
                  11.71,
                  51.7,
                  122.1
                  ]
example_list_2 = [10,
                  5,
                  1,
                  1
                  ]   
print CKK(example_list_1)
print CKK(example_list_2)
print CKK(example_list_1)
# 27.64  # Correct (122.1) - (1.4, ...)
# 3  # Correct (10) - (5, 1, 1)
# 122.1  # Something went wrong

2 个答案:

答案 0 :(得分:1)

你可以设置一个标志为None,并在CKK中传递任何值,就像使用默认的arg一样:

import copy

def prune(CKK_branch):
    def pruner(list_, flag=None):
        internal_list = copy.deepcopy(list_)
        if flag is None:
            pruner.min = float("inf")
        # Check whether end of branch or whether optimal solution achieved
        if pruner.min == 0.:
            return pruner.min
        elif len(internal_list) == 1:
            return list_[0]
        # Sort branch
        internal_list.sort(reverse=True)    
        # Prune branch
        if pruner.min < internal_list[0] - sum(internal_list[1:]):
            return internal_list[0]  
        # Find minimum in branch and track minimum of all branches
        min_branch = CKK_branch(internal_list)
        pruner.min = min(pruner.min, min_branch)
        return min_branch
    pruner.__name__ = CKK_branch.__name__
    return pruner


@prune
def CKK(list_):
    internal_list = copy.deepcopy(list_)
    # Replace maximum two numbers by their difference and their sum in two
    # branches
    diff = internal_list[0] - internal_list[1]
    sum_ = internal_list[0] + internal_list[1]
    sum_tree = copy.deepcopy(internal_list)
    diff_tree = copy.deepcopy(internal_list)
    del sum_tree[0:2]
    del diff_tree[0:2]
    sum_tree.append(sum_)
    diff_tree.append(diff)
    return min(CKK(diff_tree,True), CKK(sum_tree,True))

每次调用min(CKK(diff_tree,True), CKK(sum_tree,True))时,您都会将最小值重置为inf。

In [26]:  CKK(example_list_1)
Out[26]: 27.639999999999993

In [27]:  CKK(example_list_2)
Out[27]: 3

In [28]:  CKK(example_list_1)
Out[28]: 27.639999999999993

答案 1 :(得分:0)

为什么不添加另一个图层?

def prune(f):
    def wrapper(branch):
        def wrapped(branch):
            ...
        wrapper.min = float('inf')
        return wrapped(branch)
    return wrapper

现在你的装饰器将返回一个调用其嵌套函数的闭包。