过滤映射与列表推导式的区别

8

过滤器/映射和列表推导式是否等价? 假设我有以下函数

def fib_gen():
    a,b = 0,1
    yield 0
    yield 1
    while True:
        a,b = b,a+b
        yield b

现在我可以使用列表推导式来列出斐波那契数列:
a = fib_gen()
print [a.next() for i in range(int(sys.argv[1]))]

假设我想列出仅为偶数的斐波那契数。 我可以使用filter/map执行以下操作:
a = fib_gen()
print filter(even, map(lambda x: a.next(), range(int(sys.argv[1]))))

如何使用列表推导式获得相同的结果?
6个回答

13
是的,map(f, L) 等价于 [f(x) for x in L]filter(f, L) 等价于 [x for x in L if f(x)]。但是,由于具有副作用的列表推导式通常不好(这里你修改了生成器的状态),因此可以使用itertools 获得更清晰的解决方案:
 a = fib_gen()
 a = itertools.islice(a, int(sys.argv[1]))
 a = itertools.ifilter(even, a)
 print list(a)

10
你可以使用生成器来存储中间结果,并在其上进行 "筛选"。
fibs = (a.next() for i in whatever)
even_fibs = [num for num in fibs if num % 2 == 0]

或者在一行中:

even_fibs = [num for num in (a.next() for i in whatever) if num % 2 == 0]

请注意,如果您想从迭代器中获取一定数量的元素,则可以使用itertools.islice代替:

from itertools import islice
fibs_max_count = int(sys.argv[1])
even_fibs = [num for num in islice(fib_gen(), fibs_max_count) if num%2 == 0]

7

filtermap可以很容易地转换为列表推导式。

以下是一个基本示例:

[hex(n) for n in range(0, 100) if n > 20]

这相当于:
list(map(hex, filter(lambda x: x > 20, range(0, 100))))

在我看来,使用推导式更易读。但是如果条件变得非常复杂,我更喜欢使用 filter

所以在你的情况下:

[n for n in itertools.islice(fib_gen(), 100) if even(n)]

我在这里使用了islice,因为这个序列是无限的。但是如果你使用生成器表达式,它也会变成一个无限流:

gen = (n for n in fib_gen() if even(n))

现在你也可以使用islice来切分序列:
print itertools.islice(gen, int(sys.argv[1]))

这样可以避免在推导式中使用 next。只要不尝试评估无限序列(如果我们在列表推导中省略 islice,就会这样做),我们就可以处理您的序列。


谢谢。那么关于包含next()的过滤表达式,您有什么想法? - Oleg Pavliv
@Oleg:哦,我正在添加一些信息,请查看我的最新添加,你会发现你也可以避免使用 next - Skurmedel
你的十六进制/范围示例不是传统的过滤映射。过滤映射可以在映射过程的结果上进行过滤,而不仅仅是在该表达式的输入上进行过滤。 - Sandra

1

我认为这不适用于生成器。为了使列表推导能够工作,你需要有:

print [a.next() for i in range(int(sys.argv[1])) if even(a.next())]

这将返回:

[1, 3, 13, 55, 233]

问题是你需要两次访问列表中的下一个数字,但第二个a.next()调用所做的事情是获取下一个数字。据我所知,不可能使用列表推导式存储a.next()的值以便两次使用。

可以实现,但要像print [n for n,i in ( (a.next(),i) for i in range(int(sys.argv[1])) ) if even(n)]一样嵌套使用,其中na.next()并被使用了两次。 - Dan D.

1
你可以使用以下代码:
a = fib_gen()
print [a.next() for i in range(int(sys.argv[1])) if i%3==0]

这是一个特殊情况,因为每个第三个斐波那契数都是偶数。


0
您可以始终仅生成偶数斐波那契数...
def evenfib():
    """ Generates the even fibonacci numbers """
    a, b = 2, 0
    while True:
        a, b = b, a+4*b
        yield a

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接