【发布时间】:2016-03-17 15:20:20
【问题描述】:
我正在尝试理解迭代器。我注意到Python documentation 认为迭代器是一种函数式构造。不是很懂。
迭代器里面不是有状态吗。所以当你调用it.__next__() 时,你改变了迭代器的状态。据我所知,对象的变异状态不被认为是函数式的,因为函数式编程强调对象/闭包的不变性和组合能力。
实际上,问题出现是因为我想编写一个接受令牌并返回迭代器的 Scheme 过程/函数。
(define tokens->iterator
(lambda ls
(lambda ()
(if (null? ls)
'*eoi*
(let ((tok (car ls)))
(set! ls (cdr ls))
tok)))))
注意我必须使用set! 来改变ls,这就是我提出这个问题的方式。
要使用它,
(define it (tokens->iterator 1 '+ 2))
为了测试它,
scheme@(guile-user)> (it)
$2 = 1
scheme@(guile-user)> (it)
$3 = +
scheme@(guile-user)> (it)
$4 = 2
scheme@(guile-user)> (it)
$5 = *eoi*
scheme@(guile-user)> (it)
$6 = *eoi*
只是为了好玩,我还将它翻译成 Python:
def tokens_to_iterator(*tup):
ls = list(tup)
def iterator():
if not ls:
return "*eoi*"
else:
tok = ls.pop(0)
return tok
return iterator
类似地,pop() 方法通过改变列表来移除并返回第一个元素。
要使用它,
it = tokens_to_iterator(1, "+", 2)
为了测试它,
>>> it()
1
>>> it()
'+'
>>> it()
2
>>> it()
'*eoi*'
>>> it()
'*eoi*'
谁能澄清一下?顺便说一句,我正在使用 Python 3 和 Guile Scheme,以防有人有兴趣尝试这些示例。
【问题讨论】:
-
“功能”和“无可变状态”不是一回事。
-
因为这个状态是隐藏在里面的,从外面无法访问;它是封装的。它会在每次 next 调用时自动更改,并保留对象的有效性。从外部观察者的角度来看,它的行为符合一些规律,是一致的,这不能从外部任意改变。这个状态只是一个实现细节。
-
@zwol 还有“无副作用”。它与“无可变状态”相同吗?为什么“功能”和“无可变状态”不一样?不是所有“函数式”语言都强调“无可变状态”吗?我在这里错过了什么吗?
-
@Will Ness OOP 不是也有数据封装吗?例如,状态被封装在一个对象中。您所能做的就是调用对象的方法。究竟是什么让迭代器发挥作用?
-
有关相关示例,请参阅
http://3e8.org/pub/scheme/doc/lisp-pointers/v1i4/p23-clinger.pdf和作者对使用生成器问题的评论。对于两个消费者,任何一个观察到的序列都是混乱的。所以,这个是脆弱的,取决于使用协议。该论文中的另一件事,流,实际上添加了 storage,生成器产生的结果被存储在其中,因此两个消费者都观察到相同的(共享)序列(因为它们从存储中提取,而不是从发电机本身)。这也是itertools.tee()所做的,IIRC。或类似的东西。