简体   繁体   English

在迭代期间交换列表元素

[英]Swapping List Elements During Iteration

I have read in several places that it is bad practice to modify an array/list during iteration.我在几个地方读到过,在迭代期间修改数组/列表是不好的做法。 However many common algorithms appear to do this.然而,许多常见的算法似乎可以做到这一点。 For example Bubble Sort, Insertion Sort, and the example below for finding the minimum number of swaps needed to sort a list.例如冒泡排序、插入排序和下面的示例,用于查找对列表进行排序所需的最小交换次数。

Is swapping list items during iteration an exception to the rule?在迭代期间交换列表项是否是规则的例外? If so why?如果是,为什么?

Is there a difference between what happens with enumerate and a simple for i in range(len(arr)) loop in this regard?在这方面, enumerate和简单for i in range(len(arr))循环之间有什么区别吗?

def minimumSwaps(arr):
    ref_arr = sorted(arr)
    index_dict = {v: i for i,v in enumerate(arr)}
    swaps = 0
    
    for i,v in enumerate(arr):
        print("i:", i, "v:", v)
        print("arr: ", arr)
        correct_value = ref_arr[i]
        if v != correct_value:
            to_swap_ix = index_dict[correct_value]
            print("swapping", arr[to_swap_ix], "with", arr[i])
            # Why can you modify list during iteration?
            arr[to_swap_ix],arr[i] = arr[i], arr[to_swap_ix]
            index_dict[v] = to_swap_ix
            index_dict[correct_value] = i
            swaps += 1
            
    return swaps
    

arr = list(map(int, "1 3 5 2 4 6 7".split(" ")))
assert minimumSwaps(arr) == 3

An array should not be modified while iterating through it, because iterators cannot handle the changes.遍历数组时不应修改数组,因为迭代器无法处理更改。 But there are other ways to go through an array, without using iterators.但是还有其他方法可以通过数组 go,而不使用迭代器。

This is using iterators:这是使用迭代器:

for index, item in enumerate(array):
   # don't modify array here

This is without iterators:这是没有迭代器的:

for index in range(len(array)):
   item = array[index]
   # feel free to modify array, but make sure index and len(array) are still OK

If the length & index need to be modified when modifying an array, do it even more "manually":如果在修改数组时需要修改长度和索引,请更“手动”进行:

index = 0
while index < len(array):
   item = array[index]
   # feel free to modify array and modify index if needed
   index += 1

Modifying items in a list could sometimes produce unexpected result but it's perfectly fine to do if you are aware of the effects.修改列表中的项目有时可能会产生意想不到的结果,但如果您知道效果,那完全没问题。 It's not unpredictable.这不是不可预测的。

You need to understand it's not a copy of the original list you ar iterating through.您需要了解它不是您正在迭代的原始列表的副本。 The next item is always the item on the next index in the list.下一项始终是列表中下一个索引上的项。 So if you alter the item in an index before iterator reaches it the iterator will yield the new value.因此,如果您在迭代器到达它之前更改索引中的项目,迭代器将产生新值。

That means if you for example intend to move all items one index up by setting item at index+1 to current value yielded from enumerate().这意味着,例如,如果您打算通过将 index+1 处的项目设置为 enumerate() 产生的当前值,将所有项目上移一个索引。 Then you will end up with a list completely filled with the item originally on index 0.然后,您将得到一个列表,该列表完全填充了最初位于索引 0 上的项目。

a = ['a','b','c','d']
for i, v in enumerate(a):
    next_i = (i + 1) % len(a)
    a[next_i] = v
print(a) # prints ['a', 'a', 'a', 'a']

And if you appending and inserting items to the list while iterating you may never reach the end.而且,如果您在迭代时将项目追加和插入到列表中,您可能永远不会到达终点。

In your example, and as you pointed out in a lot of algorithms for eg combinatoric and sorting, it's a part of the algorithm to change the forthcoming items.在您的示例中,正如您在许多算法中指出的那样,例如组合和排序,它是更改即将到来的项目的算法的一部分。

An iterator over a range as in for i in range(len(arr)) won't adapt to changes in the original list because the range is created before starting and is immutable.for i in range(len(arr))这样的范围内的迭代器不会适应原始列表中的变化,因为范围是在开始之前创建的并且是不可变的。 So if the list has length 4 in the beginning, the loop will try iterate exactly 4 times regardless of changes of the lists length.因此,如果列表的开头长度为 4,则无论列表长度如何变化,循环都会尝试精确迭代 4 次。

# This is probably a bad idea
for i in range(len(arr)):
    item = arr[i]
    if item == 0:
        arr.pop()

# This will work (don't ask for a use case)
for i, item in enumerate(arr):
    if item == 0:
        arr.pop()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM