2016-12-20 258 views
2

考虑下面的代码段,其产生阵列的大小为k的所有子集[1,2,3,...,N]:逻辑错误

def combinations(n, k): 
    result = [] 
    directed_combinations(n, k, 1, [], result) 
    return result 

def directed_combinations(n, k, offset, partial_combination, result): 
    if len(partial_combination) == k: 
     new_partial = [x for x in partial_combination] 
     result.append(new_partial) 
     return 
    num_remaining = k - len(partial_combination) 
    i = offset 
    #    kind of checks if expected num remaining is no greater than actual num remaining 
    while i <= n and num_remaining <= n - i + 1: 
     partial_combination.append(i) 
     directed_combinations(n, k, i + 1, partial_combination, result) 
     del partial_combination[-1] 
     # partial_combination = partial_combination[:-1] <-- same funcationality as line above, but produces weird bug. 
     i += 1 

print(combinations(n=4,k=2)) 

例如,combinations(n=4,k=2)将生成[1,2,3,4]长度为2的所有子集。 代码中有两行产生一个删除最后一个元素的列表。我尝试用del完成它,并通过切掉最后一个元素(即[-1])创建一个全新的列表。与del版本产生正确的结果。但是,与[-1]版本不。没有运行时错误;只是一个逻辑错误(即错误的结果)。

我怀疑这与创建一个新的清单时做切片与保持相同的列表del有关。我似乎无法理解为什么这是一个问题。

回答

4

我起初没有注意到你的函数是递归的(应该更好地阅读你的标签)。

你说得对,功能上两者都是差不多一样。这里是确切同样的事情:

# del partial_combination[-1]      # working (mutate) 
# partial_combination = partial_combination[:-1] # different (rebind) 
partial_combination[:] = partial_combination[:-1] # same (mutate) 

每个结果上面将是你最终包含相同的元素列表。但是,虽然delpartial_combination[:]mutate您的原始列表,中间的将名称重新绑定到具有相同元素的新列表。当您将这个新列表传递给下一个递归步骤时,它将在其自己的副本上运行,而不是在之前的递归级别正在处理的单个列表上运行。

为证明这一点,您可以在每个上述选项后调用print(id(partial_combination)),并看到重新绑定情况下的id更改,而在整个更改的情况下保持不变。

+1

好的。这说得通。然而,起初,我很困惑为什么最后一个选项突变了列表而不是创建一个新列表。我以前从来没有见过分配给切片。检查[docs(3.1.3节)](https://docs.python.org/3/tutorial/introduction.html)为我阐明了它。 –

+1

@AzaTulepbergenov [这里是一个额外的来源](http://nedbatchelder.com/text/names.html)了解变异VS重新绑定。 –