多地图迭代

时间:2019-07-11 19:21:34

标签: python lazy-evaluation

我有一个关于Python(3)在计算多个地图时如何在内部循环的问题。这是个废话的例子:

from random import randint

A = [randint(0,20) for _ in range(100)]
map1 = map(lambda a: a+1, A)
map2 = map(lambda a: a-1, map1)
B = list(map2)

因为map()产生了一个惰性表达式,在调用list(map2)之前,实际上没有任何计算,对吗?

当它最终完成计算时,更类似于以下哪种方法?

循环方法1:

A = [randint(0,20) for _ in range(100)]
temp1 = []
for a in A:
    temp1.append(a+1)

B = []
for t in temp1:
    B.append(t-1)

循环方法2:

A = [randint(0,20) for _ in range(100)]
B = []
for a in A:
    temp = a+1
    B.append(temp-1)

还是以完全不同的方式进行计算?

2 个答案:

答案 0 :(得分:1)

通常,map()函数会生成一个生成器,该生成器反过来不会生成任何输出或计算任何东西,除非明确要求。将生成器转换为列表本质上类似于向其询问下一个元素,直到没有下一个元素为止。

我们可以在命令行上做一些实验,以了解更多信息:

>>> B = [i for i in range(5)]
>>> map2 = map(lambda b:2*b, B)
>>> B[2] = 50
>>> list(map2)
[0, 2, 100, 6, 8]

我们可以看到,即使在创建生成器之后修改B ,我们的更改仍会反映在生成器的输出中。因此,似乎map保留了对原始可迭代对象的引用,并且仅在被要求时一次计算一个值。


在您的示例中,这意味着过程如下:

A = [2, 4, 6, 8, 10]
b = list(map2)
    b[0] --> next(map2) = (lambda a: a-1)(next(map1))
             --> next(map1) = (lambda a: a+1)(next(A)) 
                 --> next(A) = A[0] = 2
             --> next(map1) = 2+1 = 3
         --> next(map2) = 3-1 = 2
    ...

用人类的术语来说,map2的下一个值是通过询问map1的下一个值来计算的。而那个是根据您最初设置的A计算出来的。

答案 1 :(得分:0)

可以通过在有副作用的函数上使用map来进行调查。一般来说,您不应该为真正的代码执行此操作,但是对于调查行为是很好的。

def f1(x):
    print('f1 called on', x)
    return x

def f2(x):
    print('f2 called on', x)
    return x

nums = [1, 2, 3]
map1 = map(f1, nums)
map2 = map(f2, map1)
for x in map2:
    print('printing', x)

输出:

f1 called on 1
f2 called on 1
printing 1
f1 called on 2
f2 called on 2
printing 2
f1 called on 3
f2 called on 3
printing 3

因此,每个函数都可能在最晚被调用;直到循环以数字1结束,f1(2)才被调用。直到循环需要映射中的第二个值时,数字2才需要做。