从第一个元素创建一个 ruby 可枚举,以及一个从前一个元素获取下一个元素的函数
create a ruby enumerable from a first element, and a function to get the next element from the previous one
在 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
但它是地道的吗?已经有这样的东西了吗?
在 Ruby 中,您将使用 Enumerable#inject
:
[7, 8, 9].inject(1){ |carry, item| 2 * carry + item }
块的 return 值将用作进位变量的下一个值。
你问的是 unfold (which is called Stream.iterate
in scala), or more generically, an anamorphism. It is the exact category-theoretical dual of a fold (which is called Enumerable#inject
in ruby) aka a catamorphism。
Is there something like this already ?
遗憾的是,核心库或标准库中没有执行此功能的方法。
But is it idiomatic ?
我可能会将其设为 Enumerator
and use Kernel#loop
而不是 while true
的单例方法,仅此而已。否则,是的,您的代码非常地道。
我们就这样称呼它 unfold
:
def Enumerator.unfold(start)
new do |y|
loop do
y << start
start = yield start
end
end
end
我不知道有什么公认的惯用方法可以做到这一点。
由于您的代码似乎无法生成您喜欢的结果,我将举例说明如何实现它。
您可以使用 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
在 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
但它是地道的吗?已经有这样的东西了吗?
在 Ruby 中,您将使用 Enumerable#inject
:
[7, 8, 9].inject(1){ |carry, item| 2 * carry + item }
块的 return 值将用作进位变量的下一个值。
你问的是 unfold (which is called Stream.iterate
in scala), or more generically, an anamorphism. It is the exact category-theoretical dual of a fold (which is called Enumerable#inject
in ruby) aka a catamorphism。
Is there something like this already ?
遗憾的是,核心库或标准库中没有执行此功能的方法。
But is it idiomatic ?
我可能会将其设为 Enumerator
and use Kernel#loop
而不是 while true
的单例方法,仅此而已。否则,是的,您的代码非常地道。
我们就这样称呼它 unfold
:
def Enumerator.unfold(start)
new do |y|
loop do
y << start
start = yield start
end
end
end
我不知道有什么公认的惯用方法可以做到这一点。 由于您的代码似乎无法生成您喜欢的结果,我将举例说明如何实现它。
您可以使用 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