简体   繁体   English

Python:列表推导产生连续的偶数子列表

[英]Python: list comprehension to produce consecutive sublists of even numbers

I'm trying to practice Python exercises, but using list comprehension to solve problems rather than the beginner style loops shown in the book. 我正在尝试练习Python练习,但是使用列表理解而不是本书中显示的初学者风格循环来解决问题。 There is one example where it asks for a list of numbers to be put into a list of even numbers only, BUT they must be in sublists so that if the numbers follow after one another without being interrupted by an odd number, they should be put into a sublist together: 有一个示例要求将数字列表仅放入偶数列表中,但必须将它们放在子列表中,以便如果数字紧跟在后而不被奇数打扰,则应将它们放在一起进入子列表:

my_list = [2,3,5,7,8,9,10,12,14,15,17,25,31,32]
desired_output = [[2],[8],[10,12,14],[32]]

So you can see in the desired output above, 10,12,14 are evens that follow on from one another without being interrupted by an odd, so they get put into a sublist together. 因此,您可以在上面的期望输出中看到,10、12、14是彼此相邻的偶数,而不会被奇数中断,因此它们被放到一个子列表中。 8 has an odd on either side of it, so it gets put into a sublist alone after the odds are removed. 8的两边都有一个奇数,因此消除了几率后,它就被单独放入子列表中。

I can put together an evens list easily using list comprehension like this below, but I have no idea how to get it into sublists like the desired output shows. 我可以使用下面的列表理解功能轻松地将一个偶数列表组合在一起,但是我不知道如何将其放入子列表,如所需的输出显示。 Could someone please suggest an idea for this using list comprehension (or generators, I don't mind which as I'm trying to learn both at the moment). 有人可以使用列表理解功能(或生成器,我不介意在尝试同时学习两者的方法)时提出一个想法。 Thanks! 谢谢!

evens = [x for x in my_list if x%2==0]
print(evens)
[2, 8, 10, 12, 14, 32]

As explained in the comments, list comprehensions should not be deemed "for beginners" - first focus on writing your logic using simple for loops. 正如评论中所解释的,列表理解不应被视为“针对初学者”-首先要关注使用简单的for循环编写逻辑。

When you're ready, you can look at comprehension-based methods. 准备就绪后,您可以查看基于理解的方法。 Here's one: 这是一个:

from itertools import groupby

my_list = [2,3,5,7,8,9,10,12,14,15,17,25,31,32]

condition = lambda x: all(i%2==0 for i in x)
grouper = (list(j) for _, j in groupby(my_list, key=lambda x: x%2))

res = filter(condition, grouper)

print(list(res))

# [[2], [8], [10, 12, 14], [32]]

The main point to note in this solution is nothing is computed until you call list(res) . 在此解决方案中要注意的要点是,在调用list(res)之前不会计算任何内容。 This is because filter and generator comprehensions are lazy. 这是因为filter和生成器的理解是懒惰的。

You mentioned also wanting to learn generators, so here is a version that's also a bit more readable, imho. 您提到过也想学习生成器,所以这里是一个可读性更高的版本,恕我直言。

from itertools import groupby


def is_even(n):
    return n%2 == 0


def runs(lst):
    for even, run in groupby(lst, key=is_even):
        if even:
            yield list(run)


if __name__ == '__main__':
    lst = [2, 3, 5, 7, 8, 9, 10, 12, 14, 15, 17, 25, 31, 32]
    res = list(runs(lst))
    print(res)

Incidentally, if you absolutely, positively want to implement it as a list comprehension, this solutions falls out of the above quite naturally: 顺便说一句,如果您绝对希望将其实现为列表理解,则此解决方案很自然地属于上述情况:

[list(run) for even, run in groupby(lst, key=is_even) if even]

If you don't want to use itertools, there's another way to do it with list comprehensions. 如果您不想使用itertools,还有另一种方法可以使用列表推导。

First, take the indices of the odd elements: 首先,获取奇数元素的索引:

[i for i,x in enumerate(my_list) if x%2==1]

And add two sentinels: [-1] before and [len(my_list)] after: 并添加两个标记: [-1]之前和[len(my_list)]之后:

odd_indices = [-1]+[i for i,x in enumerate(my_list) if x%2==1]+[len(my_list)]
# [-1, 1, 2, 3, 5, 9, 10, 11, 12, 14]

You have now something like that: 您现在有类似的内容:

 [2,3,5,7,8,9,10,12,14,15,17,25,31,32]
^---^-^-^---^-----------^--^--^--^----^

You can see your sequences. 您可以看到您的序列。 Now, take the elements between those indices. 现在,采用这些索引之间的元素。 To do that, zip odd_indices with itself to get the intervals as tuples: 要做到这一点, odd_indices自己将odd_indices压缩为元组:

zip(odd_indices, odd_indices[1:])
# [(-1, 1), (1, 2), (2, 3), (3, 5), (5, 9), (9, 10), (10, 11), (11, 12), (12, 14)]    

even_groups = [my_list[a+1:b] for a,b in zip(odd_indices, odd_indices[1:])]
# [[2], [], [], [8], [10, 12, 14], [], [], [], [32]]

You just have to filter the non empty lists: 您只需要过滤非空列表:

even_groups = [my_list[a+1:b] for a,b in zip(odd_indices, odd_indices[1:]) if a+1<b]
# [[2], [8], [10, 12, 14], [32]]

You can merge the two steps into one comprehension list, but that is a bit unreadable: 您可以将两个步骤合并到一个理解列表中,但这有点难以理解:

>>> my_list = [2,3,5,7,8,9,10,12,14,15,17,25,31,32]
>>> [my_list[a+1:b] for l1 in [[-1]+[i for i,x in enumerate(my_list) if x%2==1]+[len(my_list)]] for a,b in zip(l1, l1[1:]) if b>a+1]
[[2], [8], [10, 12, 14], [32]]

As pointed by @jpp, prefer basic loops until you feel comfortable. 正如@jpp所指出的那样,在您感到舒适之前,最好选择基本循环。 And maybe avoid those nested list comprehensions forever... 也许永远避免那些嵌套的列表理解...

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

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