为啥 map over 一个 iterable 返回一个一次性的 iterable?

Posted

技术标签:

【中文标题】为啥 map over 一个 iterable 返回一个一次性的 iterable?【英文标题】:Why does map over an iterable return a one-shot iterable?为什么 map over 一个 iterable 返回一个一次性的 iterable? 【发布时间】:2017-08-25 08:10:09 【问题描述】:

为什么map 使用可以多次迭代的对象调用时不返回也可以多次迭代的对象?我认为后者更合理。

我的用例是我有很多data,所以它只能被迭代。 map(理论上)非常适合在 data 上进行操作,因为它很懒惰。但是在下面的示例中,我希望长度两次都相同。

iterable = [1,2,3,4]  # this can be iterated repeatedly
m = map(lambda x:x**2, iterable) # this again should be iterable repeatedly
print(len(list(m))) # 4
print(len(list(m))) # 0

如何映射可迭代结构并返回可迭代结构?

编辑: 这是一个imho应该如何工作的例子,展示了惰性评估:

def g(): 
    print('g() called')

data = [g, g]

# map is lazy, so nothing is called
m = map(lambda g: g(), data)
print('m: %s' % len(list(m))) # g() is called here
print('m: %s' % len(list(m))) # this should work, but doesnt

# this imap returns an iterable
class imap(object):
    def __init__(self, fnc, iterable):
        self.fnc = fnc
        self.iterable = iterable
    def __iter__(self):
        return map(self.fnc, self.iterable)

# imap is lazy, so nothing is called
im = imap(lambda g: g(), data)    
print('im: %s' % len(list(im))) # g() is called here
print('im: %s' % len(list(im))) # works as expected

【问题讨论】:

如果你需要使用len,那么为什么不直接使用列表解析呢?它同样懒惰并产生永久性结果。如果您需要len 但真的想使用map,只需在输入上调用len 而不是map 的输出。 如果您希望能够反复迭代它,为什么不然后使用列表?还是只使用列表推导? 例如,可能它消耗了太多的内存。很确定len只是作为一个例子来表明它不能被消费两次。 【参考方案1】:

为什么当使用可以多次迭代的对象调用map时不返回也可以多次迭代的对象?

因为没有接口告诉一个对象是否可以重复迭代。 map 无法判断它正在迭代的事物是否支持重复迭代,除非 map 设法以某种方式确定此信息并发明一个 API 将其公开给用户,否则 map 用户将无法判断他们的map 对象是否支持重复迭代。

此外,随着重复迭代,需要重复函数评估或缓存结果(但如果您要缓存结果,为什么要重新设计 map 以返回一个迭代器呢?)。重复的函数评估效率低下,有潜在的危险,而且通常不是用户想要的。如果用户想再次迭代,最好让用户显式重复map 调用或显式调用list

如果map 对象总是只是迭代器,那就更简单了。

【讨论】:

澄清一下:x is iter(x) 可用于确认一个可迭代对象不可 可重复,但您 can't prove the converse。 @ZeroPiraeus:问题中甚至还有一个反例:问题自己的imap 类有x is not iter(x),但仅在底层可迭代支持的情况下才支持重复迭代。 第一段是我的问题的答案——在 python 集合中是不可能的。您的第二点是权衡所有其他实现映射的语言(Haskell、Java、Scala、Closoure 等)必须以某种方式面对和解决。我找到了 PyFunctional 库,它似乎可以满足我的需要。

以上是关于为啥 map over 一个 iterable 返回一个一次性的 iterable?的主要内容,如果未能解决你的问题,请参考以下文章

[Immutable,js] Iterating Over an Immutable.js Map()

c++11中,为啥可以用const_iterator删除map/multimap中的元素

std::map<t1, t2>::erase(iterator position) 的工作?

为啥这个Iterable在映射后会产生一个Set?

java 中 iterator 为啥翻译成迭代器呢

mapreducefilter 的用法