【问题标题】:how to pop all negative numbers from list with for loop without list comprehension in python如何在python中没有列表理解的情况下使用for循环从列表中弹出所有负数
【发布时间】:2020-11-27 03:50:07
【问题描述】:

我收到一条错误消息说 “回溯(最近一次通话最后一次): 文件“main.py”,第 10 行,在 如果列表 1[j]

我正在尝试从示例输入“10 -7 4 39 -6 12 2”中删除所有负数,以生成类似 10 4 39 12 2 的输出,并尝试使用 pop 来执行此操作。

我无法找到一个确切的解决方法,我通过谷歌搜索并在 stackoverflow 上搜索。

我不希望列表理解作为答案

input1 = str(input())

list1 = input1.split()

for i in range(len(list1)):
    list1[i] = int(list1[i])


for j in range(len(list1)):
        if list1[j] < 0:
            list1.pop(j)

print(list1)

【问题讨论】:

  • 你为什么要使用pop?由于您没有保存该值,因此您可能应该使用del list1[index]。在任何 cse 中,在迭代列表时修改列表是 1) 正确执行令人讨厌 2) 非常低效。您应该改为创建一个新列表,无论是使用列表理解还是使用常规循环
  • 我还没有学习列表理解,但我正在尝试弄清楚如何使用 for 循环来做到这一点,我过去用 pop 做过很多次,但现在用 pop 解决了我似乎无法弄清楚我只是想知道它,所以我知道该怎么做,以防我将来需要这样做,我最终将学习列表理解。我尝试了“del list1[j]”,它仍然显示相同的错误消息。
  • 是否使用列表理解并不是真正的问题。困难在于在您循环的同一个列表上使用pop(),这很少是一个好主意。如果必须使用循环,通过选择性地append()ing 到一个新列表来创建一个新列表几乎总是更好。
  • @Jamesx1080 再次,问题是没有使用列表理解。列表推导本质上是附加到列表的循环的简写。就做那个循环。但是你应该通过创建一个新列表来做到这一点。我已经写了一个答案,向您展示了如何可以这样做,但几乎从不有充分的理由这样做。所以不要。
  • 如果我没有正确回答您的问题,我很抱歉,但是如果我创建一个新列表并在那里添加所有正数可以吗?

标签: python for-loop


【解决方案1】:

您为什么要尝试使用pop?由于您没有保存值,因此您可能应该使用del list1[index]

在任何情况下,在迭代列表时修改列表是 1) 正确执行令人讨厌 2) 非常低效。您应该改为创建一个新列表,无论是使用列表推导式还是使用常规循环。

根本问题是,当您删除一个项目时,之后的所有索引都发生了变化

天真地,你可能认为你可以做这样的事情:

for i, e in enumerate(data):
    if e < 0:
        del data[i]

这不会引发索引错误,但是,如果您有两个相邻的负数,它将跳过一个:

>>> data = [1,2, -3, -4, 5]
>>> for i,e in enumerate(data):
...     if e < 0:
...         del data[i]
...
>>> data
[1, 2, -4, 5]
>>>

但您可以通过以下几种方式做到这一点。

请记住,这些算法效率低下

首先,只需跟踪您已删除的项目数量并相应地调整索引:

>>> removed = 0
>>> data = [1,2, -3, -4, 5]
>>> for i in range(len(data)):
...     if data[i - removed] < 0:
...         del data[i - removed]
...         removed += 1
...
>>> data
[1, 2, 5]

实际上,也许最简单的方法是使用 while 循环:

>>> data = [1,2, -3, -4, 5]
>>> i = 0
>>> while i < len(data):
...     if data[i] < 0:
...         del data[i]
...     else:
...         i += 1
...
>>> data
[1, 2, 5]

上面的重点是仅在您不删除某些内容时增加i

最后,您可以简单地从列表末尾开始迭代:

>>> data = [1,2, -3, -4, 5]
>>> for i in reversed(range(len(data))):
...     if data[i] < 0:
...         del data[i]
...
>>> data
[1, 2, 5]
>>>

重申:

以上算法都非常低效。

它们需要线性空间和二次时间。您可以在线性空间和线性时间中创建一个新列表。

考虑:

>>> data = [1,2, -3, -4, 5]
>>> new_data = []
>>> for x in data:
...     if x >= 0:
...         new_data.append(x)
...
>>> new_data
[1, 2, 5]

或等效的列表推导:

>>> data = [x for x in data if x >= 0]
>>> data
[1, 2, 5]

【讨论】:

    【解决方案2】:

    代码:

    input1 = str(input())
    
    list1 = input1.split()
    
    ind_to_remove = []
    for i in range(len(list1)):
        list1[i] = int(list1[i])
        if list1[i] < 0:
            ind_to_remove.append(i)
    
    ind_to_remove.sort(reverse=True)
    for ind in ind_to_remove:
        list1.pop(ind)
    
    print(list1)
    

    【讨论】:

    • 这行不通。考虑list1 = [1,2,-3,-4,5] 甚至[1, -2, 3, -4, 5]。事实上,[1, -2, 3, 4, -5] 实际上会抛出一个错误。
    • 添加了 ind_to_remove.sort(reverse=True)
    • 好吧,你可以只做for ind in reversed(ind_to_remove):,但至少上述方法可行。
    【解决方案3】:

    列表推导式可能是最佳实践,但如果没有列表推导式,您也可以这样做。您正在做的是遍历初始列表的长度,但 pop 正在缩小您的列表,以便原始索引不再适用于列表(因此出现错误)。

    一种方法是在列表中使用.remove。这将删除与该值匹配的第一个元素,这将在这种情况下起作用。这更像是一个 hack,因为我们使用 list() 函数返回列表的副本,以便在我们修改它时迭代副本并从列表中获取每个值。如果我们不创建副本,循环将跳过一些值。如果您不想要列表理解或创建新列表,这可能比此处发布的其他方法更干净(但是您实际上是在内存方面使用此方法创建新列表)。

    lst = ["1", "-3", "-5", "-3", "10", "10", "11", "-4"]
    
    for val in list(lst):
        if int(val) < 0:
            lst.remove(val)
        else:
            lst[lst.index(val)] = int(val)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-05
      • 2019-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-25
      相关资源
      最近更新 更多