2015-06-29 91 views
16

考慮以下列表:交換兩個子列表列表中

my_list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 

我希望能夠與子列表my_list[7:10]儘快和儘可能高效地交換子列表my_list[2:4],得到新名單:

new_list=[0, 1, 7, 8, 9, 4, 5, 6, 2, 3, 10, 11, 12] 

這裏是我的嘗試:

def swap(s1, s2, l): 

    seg1=l[:s1.start]+l[s2] 
    seg2=l[s1.stop : s2.start] 
    seg3=l[s1]+l[s2.stop:] 

    return seg1+seg2+seg3 


print swap(slice(2,4), slice(7,10), [0,1,2,3,4,5,6,7,8,9,10,11,12]) 

這並打印德儘管這樣做對我來說看起來很糟糕。

是否有一個更簡單和優雅的方式來做,它不會爲每個函數調用創建四個新列表? (我打算稱此功能很多)

我不介意(實際上我更喜歡)更改原始列表,而不是每個函數調用都創建新實例。

+2

my_list [2:4] = my_list [7:10]'? – Moritz

+2

這將覆蓋'my_list [2:4]'。 –

+0

我不明白,因爲它創建:'[0,1,7,8,9,4,5,6,7,8,9,10,11,12]' – Moritz

回答

25

切片可以分配。

兩個變量可以與a, b = b, a交換。

結合上述兩種::

>>> my_list[7:10], my_list[2:4] = my_list[2:4], my_list[7:10] 
>>> my_list 
[0, 1, 7, 8, 9, 4, 5, 6, 2, 3, 10, 11, 12] 

要注意的是 - 如果片有不同的尺寸 - 的順序很重要:如果以相反的順序調換,你結束了一個不同的結果,因爲它會首先改變初始項目(較低的索引),然後較高的索引項目(但是那些將通過第一次分配在不同的位置移動)。

另外,切片不能重疊。

+0

太好了。這看起來好多了,簡單了,它也改變了原來的 - 這是可取的。謝謝。 –

+3

是的,你不能以相反的順序交換列表。如果使用函數,我們可以首先判斷兩個範圍的邊界。 – Will

+1

一個失敗的角落案例是重疊的範圍......但我不確定他們是否會在任何情況下產生任何有意義的結果...... – Bakuriu

0

我認爲使用列表索引作爲參數要好得多。

如果你想重新定義你的替換機能的研究是這樣的:

my_list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 

def slice_replace(src_list, l_start, l_end, r_start, r_end): 
    if l_end <= r_start: 
     return src_list[:l_start] + src_list[r_start:r_end] + src_list[l_end:r_start] + src_list[l_start:l_end] + src_list[r_end:] 
    else: 
     return slice_replace(src_list, r_start, r_end, l_start, l_end) 

print my_list 
new_list = slice_replace(my_list, 2, 4, 7, 10) 
print new_list 

new_list = slice_replace(my_list, 7, 10, 2, 4) 
print new_list 

我有固定它。

+1

這不可能正常工作。它只執行三個切片,但總共需要組合5個片段(兩個給定切片,以及它們之前/之間/之後的列表部分)。 – interjay

+0

@interjay爲什麼5段?恐怕我沒有看到這一點。 – Will

+0

嘗試運行您的代碼並查看它是否提供了所需的結果。 – interjay

1

我認爲最好使用串聯和切片。如果您通過列表,然後使用索引對傳遞兩個列表,則可以將列表分開並重新排列兩個子列表。請注意,indexAindexB都以某種方式作爲常規切片工作,包含起始編號,但最後一個不包含。

def replace(source, indexA, indexB): 
    newList = source[:indexA[0]] + source[indexB[0]:indexB[1]] 
    newList += source[indexA[1]:indexB[0]] + source[indexA[0]:indexA[1]] 
    newList += source[indexB[1]:] 
    return newList 

myList = replace(myList, [2,4], [7,10]) 
5

您可以使用正常的交換技術(x,y = y,x)這裏,但只有當你以正確的順序進行交換:x必須是第二個(最右邊)的片段,而y是第一個(最左邊)切片。

>>> my_list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 
>>> my_list[7:10], my_list[2:4] = my_list[2:4], my_list[7:10] 
>>> my_list 
[0, 1, 7, 8, 9, 4, 5, 6, 2, 3, 10, 11, 12] 

這工作,因爲它會分配給my_list[7:10]第一,然後纔是my_list[2:4]

如果以相反順序執行此操作,首先分配給my_list[2:4]會由於子列表具有不同的長度而導致右側的項目位置發生變化,從而導致錯誤結果。

從性能上看,這可能會或可能不會比您的代碼更快:它可能取決於列表和切片的長度。您將不得不在典型的使用案例中進行測試以查看。

1

這是一個其他的方式來做到這一點:

import itertools 
my_list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 
my_list2 = [] 
my_list2.extend((my_list[0:2],my_list[7:10],my_list[4:7],my_list[2:4],my_list[10:])) 
new_list = list(itertools.chain.from_iterable(my_list2) 

new_list打印輸出:

[0, 1, 7, 8, 9, 4, 5, 6, 2, 3, 10, 11, 12] 
2

不完全是顯而易見的(或有效的),但它的工作原理。我很好奇,看是否可以進一步利用切片對象。

import itertools 

def replace(s1, s2, l): 
    lslice = [slice(0,s1.start), s2, slice(s1.stop, s2.start), s1, slice(s2.stop,len(l))] 
    return list(itertools.chain.from_iterable([l[x] for x in lslice]))