在Scala中,您可以从第一个元素定义一个流,并从前一个元素中获取下一个元素的函数;
Stream.iterate(1)(x => 2 * x + 1)
在Ruby中存在这样的东西吗?
当然我们可以手动滚动它 -
module Kernel
def stream
Enumerator.new do |y|
that = self
while true
y << that
that = yield that
end
end
end
end
但它是惯用的吗?有没有这样的东西?
答案 0 :(得分:3)
您所询问的内容称为unfold(在Stream.iterate
中称为scala),或更一般地称为anamorphism。它是fold(在Enumerable#inject
中称为ruby)又称catamorphism的确切类别 - 理论对偶。
有没有这样的东西?
不幸的是,核心或标准库中没有执行此功能的方法。
但它是惯用的吗?
我可能会将它设为Enumerator
的单例方法并使用Kernel#loop
代替while true
,但这是关于它的。否则,是的,您的代码非常惯用。
虽然我们正在使用它,但我们称之为unfold
:
def Enumerator.unfold(start)
new do |y|
loop do
y << start
start = yield start
end
end
end
答案 1 :(得分:1)
我不知道这样做的惯用方法。 由于您的代码似乎没有产生您喜欢的内容,因此我将向您举例说明如何实现它。
您可以使用Fiber
创建外部迭代展开,如下所示:
def stream(init, &block)
Fiber.new do
memo = init
loop do
Fiber.yield memo
memo = block.call memo
end
end
end
a = stream(1){ |x| 2 * x + 1 }
a.resume # => 1
a.resume # => 3
a.resume # => 7
a.resume # => 15
如果您希望Enumerator::Lazy
拥有所有超级大国,请考虑以下事项:
def stream2(init, &block)
Enumerator.new do |yielder|
memo = init
loop do
yielder << memo
memo = block.call memo
end
end.lazy
end
a2 = stream2(1){ |x| 2 * x + 1 }
a2.next # => 1
a2.next # => 3
a2.next # => 7
a2.take(10).force # => [1, 3, 7, 15, 31, 63, 127, 255, 511, 1023]
其行为与此相同:
def stream3(init, &block)
memo = init
(1..Float::INFINITY).lazy.map{ |_| memo = block.call(memo) }
end
最后一个可能是最惯用的,并且最好隐藏枚举的内部。但是,我个人不喜欢stream3
,因为它只会产生数字以丢弃它们。
HTH
答案 2 :(得分:-3)
在Ruby中,您将使用Enumerable#inject
:
[7, 8, 9].inject(1){ |carry, item| 2 * carry + item }
块的返回值将用作进位变量的下一个值。