10

我编写了一个函数来从列表中删除奇数,如下所示:

def remove_odd(l):
    for i in l:
        if i % 2 != 0:
            l.remove(i)
    print l
    return l

remove_odd([4,5,4])
remove_odd([4,5,4,7,9,11])
remove_odd([4,5,4,7,9,11,12,13])

它返回:

[4, 4]
[4, 4, 9]
[4, 4, 9, 12]

-> 错误

但是当我更改为删除偶数时:

def remove_even(l):
    for i in l:
        if i % 2 == 0:
            l.remove(i)
    print l
    return l

remove_even([4,5,4])
remove_even([4,5,4,7,9,11])
remove_even([4,5,4,7,9,11,12,13])

答案是可以的:

[5]
[5, 7, 9, 11]
[5, 7, 9, 11, 13]

remove_odd() 函数有什么问题?我知道人们通常会在 func 中创建第二个列表,然后将偶数附加到该列表中,但是我们可以用 list.remove() 解决这个练习吗?

谢谢!

4

9 回答 9

17

您的功能以不同于您预期的方式工作。循环采用第for一个元素,而不是第二个等,因此当您删除一个元素时,其他元素会改变它们的位置,并且当它们前面有另一个奇数时可以被它跳过(在您的情况下会发生这种情况)。

如果您坚持使用.remove()方法,则必须改为对副本进行操作,如下所示:

def remove_odd(1):
    for i in l[:]:
        if i % 2 != 0:
            l.remove(i)
    return l

l[:]是 list 的浅拷贝l

但是,我认为使用列表理解会更清晰:

def remove_odd(l):
    return [x for x in l if x % 2 == 0]
于 2013-01-11T09:37:35.737 回答
6

remove_odd() 函数有什么问题?

您在更改列表大小的同时迭代列表。这导致它跳过一个或多个元素

你为什么不使用列表理解。它更具 Pythonic 和可读性

def remove_odd(l):
    return [e for e in l if e % 2 == 0]

remove_odd([4,5,4,7,9,11])
[4, 4]

同样,您可以编写您的 remove_even 例程

def remove_even(l):
    return [e for e in l if e % 2]

remove_even([4,5,4,7,9,11])
[5, 7, 9, 11]
于 2013-01-11T09:32:27.553 回答
5

Python为此提供了一个内置方法:filter

filtered_list = filter(lambda x: x%2==0, input_list)

在 Python 3 中要小心,因为这里的 filter 只是一个生成器,所以你必须写:

filtered_list = list(filter(lambda x: x%2==0, input_list))
于 2013-01-11T09:37:57.233 回答
3

您在迭代列表时尝试修改列表。

尝试这样的事情:

In [28]: def remove_odd(l):
    return [x for x in l if x%2 == 0]
   ....: 

In [29]: remove_odd([4,5,4,7,9,11])
Out[29]: [4, 4]

In [30]: remove_odd([4,5,4,7,9,11,12,13])
Out[30]: [4, 4, 12]

或仅修复您的代码,您应该迭代l[:].

l[:]返回一个浅拷贝,l其等价于list(l).

In [38]: def remove_odd(l):
        for i in l[:]:
                if i % 2 != 0:
                      l.remove(i)
        return l
   ....:     

In [39]: remove_odd([4,5,4,7,9,11,12,13])
Out[39]: [4, 4, 12]

In [40]: remove_odd([4,5,4,7,9,11])
Out[40]: [4, 4]
于 2013-01-11T09:32:50.313 回答
2

修改整个列表的最佳方法是使用它的副本:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l=range(10)
>>> type(l)
<type 'list'>
>>> l[:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> type(l[:])
<type 'list'>
>>>

从关闭文档:

如果您需要在循环内修改您正在迭代的序列(例如复制选定的项目),建议您首先制作一个副本。对序列进行迭代不会隐式生成副本。切片表示法使这特别方便:

   >>>>>> for w in words[:]:  # Loop over a slice copy of the entire list.
    ...     if len(w) > 6:
    ...         words.insert(0, w)
    ...
    >>> words
    ['defenestrate', 'cat', 'window', 'defenestrate']

http://docs.python.org/2/tutorial/controlflow.html

特别是对于您的示例:

def remove_odd(l):
    for i in l[:]:
        if i % 2:
            l.remove(i)
    return l

工作得很好。

于 2013-01-11T09:36:28.680 回答
1

enumerate如果您在示例中使用,您可以理解发生了什么。

def remove_odd(l):
    for n, i in enumerate(l):
        print n, i
        if i % 2 != 0:
            l.remove(i)
    print l
    return l

remove_odd([4,5,4,7,9,11])

它给出了结果:

0 4
1 5
2 7
3 11
[4, 4, 9]

因此,在第一种和第二种情况下,for 循环使用正确的值 4 和 5。但是您从l. 然后在第三步你叫 7 而不是第三位置的 4。因此l,正如其他答案已经建议的那样,最好复制。

于 2013-01-11T09:47:29.863 回答
1
import numpy as np
rand_vec3 = np.arange(0,10)
print(rand_vec3)
print(rand_vec3[rand_vec3 % 2 == 0])
于 2021-03-28T04:00:10.717 回答
1

你可以试试这样..

def remove_odd(l):
    a=[]
    for i in range(len(l)):
        if(l[i]%2 ==0):
            a.append(l[i])
    return a
print remove_odd([1,2,2,6,4,1,3])
于 2015-08-13T20:57:21.110 回答
0

Here 另一种实现和一些性能测试,实际目标是不更改实际列表,但删除同一列表中的赔率数

第一种方法

# --- Naive Approach Brute Force
def remove_odd(array):
    """Very unefficient Funciton, it has to copy the arr then iterate through it, then calls
    remove function, however it does the job, good for small Lists"""
    array_copy = array.copy()
    for n in array_copy:
        if n % 2 != 0:
            array.remove(n)
    return array

tests = [[4,5,4], [4,5,4,7,9,11], [4,5,4,7,9,11,12,13]]
for test in tests:
    print(f'List Before --> {test}')
    result = remove_odd(test)
    print(f'List After --> {test}')
    print('===='*15)

## Other Solution ##

def remove_odd(array):
    """Better Solution, it iterates through the Array once"""
    idx = 0
    offset = 0  # NOTE: Offset keeps tracks of the jumps after each iteration
    max_iter = len(array)
    while max_iter:
        n = array[idx-offset]

        if n % 2 != 0:
            offset += 1
            array.remove(n)

        idx += 1
        max_iter -= 1

tests = [[4,5,4], [4,5,4,7,9,11], [4,5,4,7,9,11,12,13]]
for test in tests:
    print(f'List Before --> {test}')
    result = remove_odd(test)
    print(f'List After --> {test}')
    print('===='*15)

输出两个函数##

List Before --> [4, 5, 4]
List After --> [4, 4]
============================================================
List Before --> [4, 5, 4, 7, 9, 11]
List After --> [4, 4]
============================================================
List Before --> [4, 5, 4, 7, 9, 11, 12, 13]
List After --> [4, 4, 12]
============================================================

基准测试

于 2021-01-20T18:44:13.407 回答