繁体   English   中英

创建一个可从第一个元素枚举的 ruby 和一个 function 从前一个元素中获取下一个元素

[英]create a ruby enumerable from a first element, and a function to get the next element from the previous one

在 Scala 中,您可以从第一个元素定义 stream ,并从前一个元素中定义一个 function 以获得下一个;

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

但它是惯用的吗? 已经有这样的事情了吗?

您要问的问题称为 (在称为Stream.iterate ),或更笼统地说是 它是的精确的类别理论对偶(在称为Enumerable#inject ),也称为同

已经有这样的东西了吗?

不幸的是,核心或标准库中没有执行此功能的方法。

但这是惯用的吗?

我可能会使其成为Enumerator的单例方法,并使用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因为它产生数字只是为了丢弃它们。

高温超导

Ruby 2.7 引入了Enumerator#produce ,它执行George Simms最初询问的 AFAICT。

所以 Ruby 相当于原始 Scala 片段:

Stream.iterate(1)(x => 2 * x + 1)

将会:

Enumerator.produce(1) { |x| 2 * x + 1 }

并导致:

irb> Enumerator.produce(1) { |x| 2 * x + 1 }.first(5)
=> [1, 3, 7, 15, 31]
irb> _

其中 - 如果您喜欢编号块参数(另一个 Ruby 2.7 功能) - 也可以写成:

irb> Enumerator.produce(1) { 2 * _1 + 1 }.first(5)
=> [1, 3, 7, 15, 31]
irb> _

在Ruby中,您可以使用Enumerable#inject

[7, 8, 9].inject(1){ |carry, item| 2 * carry + item }

该块的返回值将用作进位变量的下一个值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM