2016-12-20 224 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重新綁定。 –