简体   繁体   English

无法在Python中使用递归从嵌套列表中删除项目

[英]Cannot remove item from nested list using recursion in Python

As the title states. 如标题所述。 I am trying to iterate through a list using recursion and remove any number in the list (including nested lists) that is equal to a given number. 我正在尝试使用递归遍历列表,并删除列表中包括给定数字的任何数字(包括嵌套列表)。 Here is what I have so far: 这是我到目前为止的内容:

def deepRemoveAll(e, L):
    if len(L) == 0:
        return L
    if type(L[0]) == type([]):
        return deepRemoveAll(e, L[0])
    if e == L[0]:
        L.pop(0)
        return deepRemoveAll(e, L)
    if e != L[0]:
        temp = L[0]
        L.pop(0)
        return [temp] + deepRemoveAll(e, L)

print(deepRemoveAll(47, [42, 47, [1, 2, [47, 48, 49], 50, 47, 51], 52]))

The code seems flawless to me but for some reason the function is returning the list [42, 1, 2, 48, 49]. 代码对我来说似乎完美无缺,但是由于某种原因,该函数返回了列表[42,1,2,48,49]。 This is incorrect because in this case, all I need to remove are any 47's contained within but it is also removing 50, 51, and 52. The nested lists also need to stay intact but this is combining everything into one and I cannot for the life of me figure out why. 这是不正确的,因为在这种情况下,我需要删除的只是其中包含的所有47,但同时也删除了50、51和52。嵌套列表也需要保持原样,但这将所有内容组合为一个,我不能删除我的生活弄清楚为什么。

This should help: 这应该有助于:

def deep_remove_all(e, a_list):
    res = []
    for item in a_list:
        if isinstance(item, list):
            res.append(deep_remove_all(e, item))
        elif item != e:
            res.append(item)
    return res

You could also write it like this: 您也可以这样写:

def deep_remove_all(e, a_list):
    res = []
    for item in a_list:
        if item == e:
            continue

        res.append( 
            deep_remove_all(e, item)
            if isinstance(item, list) 
            else item
        )

    return res

Look at these two lines: 看这两行:

if type(L[0]) == type([]):
    return deepRemoveAll(e, L[0]);

What you are saying here is: 您在这里说的是:

If the first element of the list is a list, then recurse on that list and throw away the rest of the elements of this list. 如果列表的第一个元素是列表,则在该列表上递归并丢弃该列表的其余元素。

For example, if you had L=[[1,2],3] , your if type(L[0]) == type([]) check would return true, and you would say deepRemoveAll(e, [1,2]) , and the 3 is gone, regardless of what e is. 例如,如果您有L=[[1,2],3]if type(L[0]) == type([])检查将返回true,并且您会说deepRemoveAll(e, [1,2]) ,无论e是什么,3都不见了。

To fix your code: 要修复您的代码:

Simply change return deepRemoveAll(e, L[0]); 只需更改return deepRemoveAll(e, L[0]); to L[0] = deepRemoveAll(e, L[0]) , so that the first element of the list gets turned into itself with all the 47's removed, and then continue on with the rest of the logic. L[0] = deepRemoveAll(e, L[0]) ,以便列表的第一个元素变成自身,而所有47都被删除,然后继续其余的逻辑。

Let's look at your code and find some stuff to fix: 让我们看一下您的代码,找到一些要修复的东西:

def deepRemoveAll(e, L):
    if len(L) == 0:
            return L;
    if type(L[0]) == type([]):
            return deepRemoveAll(e, L[0]);
    if e == L[0]:
            L.pop(0);
            return deepRemoveAll(e, L);
    if e != L[0]:
            temp = L[0];
            L.pop(0);
            return [temp] + deepRemoveAll(e, L);

print(deepRemoveAll(47, [42, 47, [1, 2, [47, 48, 49], 50, 47, 51], 52]));

The first thing I see is just a quibble: type([]) . 我看到的第一件事只是一个小问题: type([]) In modern Python, that's list . 在现代Python中,这就是list

But before we change that, I notice you're doing a lot with L[0] , and you almost always pop it. 但是在我们更改它之前,我注意到您正在使用L[0]做很多事情,并且几乎总是pop它。 Let's make that common: 让我们变得常见:

def deepRemoveAll(e, L):
    if len(L) == 0: return L

    l0 = L.pop(0)

Now we've got l0 (so not more repeated indexing) and it's out. 现在我们有了l0(因此不再重复索引)了。 We can put it back, or not, as we decide. 我们可以决定是否放回它。

if type(l0) == list:

That will fix the type([]) above. 这将修复上面的type([]) But what do we do if the first element is a sublist? 但是,如果第一个元素是子列表,我们该怎么办? Obviously, we replace the first element with a deep-cleaned version of the first element: 显然,我们用第一个元素的深化版本替换了第一个元素:

if type(l0) == list:
    l0 = deepRemoveAll(e, l0)

It's still going to be the first element, but now we know it's clean. 它仍然将是第一个元素,但是现在我们知道它是干净的。

Your next steps are to check if l0 is the e we want to remove. 下一步是检查l0是否是我们要删除的e If so, you don't replace the start of the list - you just return cleaned-up remainder. 如果是这样, 则无需替换列表的开头-您只需返回清理后的余数即可。 That's correct, and it's also mutually exclusive with being a sub-list, so we can use an else or elif : 没错,并且作为子列表也是互斥的,因此我们可以使用elseelif

elif l0 == e:
    return deepRemoveAll(e, L)

And finally, there's the case where l0 != e . 最后,有l0 != e In this case, we want to clean the rest of L, and stick the l0 value back at the front. 在这种情况下,我们要清洁其余的L,并将l0值重新贴在前面。

But wait! 可是等等! That's also what we want to do in the case where l0 was a list, remember? 在l0是列表的情况下,这也是我们想要做的,还记得吗? All we did so far is clean up l0. 到目前为止,我们所做的只是清除l0。

So let's fall out of the if/elif, so that we can merge the top code (l0 is a list) with the else code that we haven't put in: l0 != e. 因此,让我们脱离if / elif,以便可以将顶部代码(l0是一个列表)与我们未放入的else代码合并:l0!= e。

return [l0] + deepRemoveAll(e, L)

Wrap it all up: 总结一下:

def deepRemoveAll(e, L):
    if len(L) == 0: return L

    l0 = L.pop(0)

    if type(l0) == list:
        l0 = deepRemoveAll(e, l0)
    elif l0 == e:
        return deepRemoveAll(e, L)

    return [l0] + deepRemoveAll(e, L)

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

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