Ruby 如何创建一个;克隆-可用于外部迭代的枚举器?

Ruby 如何创建一个;克隆-可用于外部迭代的枚举器?,ruby,clone,enumerator,Ruby,Clone,Enumerator,我想通过clone-able为外部迭代创建一个枚举器,以便克隆保留当前的枚举状态 例如,假设我有一个方法返回一个枚举数,该枚举数产生: 我想列举前5个平方数,对于每个值,打印后面的3个平方数。一些与相关的琐碎内容: square_numbers.take(8).each_cons(4) do |a, *rest| printf("%2d: %2d %2d %2d\n", a, *rest) end 输出: 1: 4 9 16 4: 9 16 25 9: 16

我想通过
clone
-able为外部迭代创建一个枚举器,以便克隆保留当前的枚举状态

例如,假设我有一个方法返回一个枚举数,该枚举数产生:

我想列举前5个平方数,对于每个值,打印后面的3个平方数。一些与
相关的琐碎内容

square_numbers.take(8).each_cons(4) do |a, *rest|
  printf("%2d: %2d %2d %2d\n", a, *rest)
end
输出:

 1:  4  9 16
 4:  9 16 25
 9: 16 25 36
16: 25 36 49
25: 36 49 64
但与上述不同,我希望使用两个嵌套循环以及
next
clone
使用外部迭代:

outer_enum = square_numbers
5.times do
  i = outer_enum.next
  printf('%2d:', i)

  inner_enum = outer_enum.clone
  3.times do
    j = inner_enum.next
    printf(' %2d', j)
  end
  print("\n")
end
不幸的是,上述克隆
的尝试引发了以下问题:

`initialize_copy':无法复制执行上下文(TypeError)
我知道Ruby并没有提供这种开箱即用的功能。但是我自己怎么能实现呢?如何创建支持克隆的
枚举器


我假设这是一个实现
initialize\u copy
并复制
n
d
的两个变量值的问题,但我不知道如何或在哪里执行。

也许您可以编写一个自己的类来满足您的要求:

类号平方器
def初始化
@n=@d=1
结束
def下一个
ret=@n
@d+=2
@n+=@d
ret
结束
结束
ns1=numbersquare.new
Array.new(5){ns1.next}
# => [1, 4, 9, 16, 25]
ns2=ns1.clone
Array.new(5){ns2.next}
# => [36, 49, 64, 81, 100]

无法复制Ruby光纤,Enumerator的C实现存储一个指向光纤的指针,该光纤似乎不会以任何方式暴露于Ruby代码中

if(ptr0->fib){
/*无法复制光纤*/
rb_raise(rb_eTypeError,“无法复制执行上下文”);
}

通过C源代码可以看出,枚举数和光纤之间有着非常深刻的联系。因此,我怀疑是否有任何方法可以改变
initialize\u copy
的行为,以允许
克隆

免责声明:我在回答我自己的问题


实现这一点的一种方法是对枚举数进行子分类。特别是,现在已弃用的接受对象和方法的变体:

class ObjectEnumerator < Enumerator
  attr_reader :object, :method

  def initialize(object, method = :each)
    @object = object
    @method = method
    super
  end

  def initialize_copy(orig)
    initialize(orig.object.clone, orig.method)
  end
end
这为我们提供了一个基本的、可克隆的枚举器:

e = NumberSquarer.new.each
#=> #<ObjectEnumerator: #<NumberSquarer:0x00007fde60915e10 @d=-1, @n=0>:each>

e.next #=> 1
e.next #=> 4

other = enum.clone
#=> #<ObjectEnumerator: #<NumberSquarer:0x00007fcf23842520 @d=3, @n=4>:each>

enum.next #=> 9
enum.next #=> 16

other.next #=> 9
e=numbersquare.new.each
#=> #
e、 下一步#=>1
e、 下一步#=>4
其他=enum.clone
#=> #
enum.next#=>9
enum.next#=>16
其他。下一步#=>9

我提供了一个不同的解决方案,它不是对问题的直接回答:

如何创建支持克隆的枚举器

但是如果我没有错的话,克隆不可克隆Ruby的
枚举器的唯一目的就是获取对枚举器中下一个对象的引用

在这种情况下,在下面的示例中,我们需要存储在
奇数和
平方中的两个值

我们可以将这些值存储在
数组中
并返回数组而不是单个值,然后我们可以使用
枚举器.peek
,以获得用于初始化新
枚举器的数组

def square_数(起动器={})
返回(_方法_,启动器)的枚举_,除非给出块_?
last_odd=starter.fetch(:square_odd[1,1])[1]
square=starter.fetch(:square_odd,[1,1])[0]
环道
收益率[平方,最后一个奇数]
最后一个奇数+=2
平方+=最后一个奇数
结束
结束
外部枚举=平方数
5.5倍
i=外部枚举。下一步[0]
printf(“%2d:”,i)
内部枚举=平方数(平方奇数:外部枚举.peek)
3.5倍
j=内部枚举。下一步[0]
printf(“%2d”,j)
结束
打印(“\n”)
结束

当然,可以选择重新实现
枚举器。但这并不是我真正想要的:-)我认为内置软件是做不到的<代码>枚举数
Proc
一样,都不是可序列化的对象。我相信这是因为它们内部包含对实时运行时内存状态的引用。通过定义自己的类,您正在设计具有明确定义的变量和无状态函数的东西,即可以复制的东西。顺便说一句,我使用一个proc和一个包含局部变量的显式绑定成功地实现了这一点。这是一个相当肮脏的黑客。。。但这是可能的。你为什么不把你所做的事情贴出来呢?我认为这只是创建类的一种迂回方式,就像我建议的()。我理解“光纤不能被复制”是指“不能自动复制”,所以不能克隆任意枚举数。但我可以完全控制我的统计员。也许这是另一个故事?好吧,注释纤维不能复制是在C源代码的枚举器,所以它听起来相当最终。
class NumberSquarer
  def initialize
    @d = -1
    @n = 0
  end

  def each
    return ObjectEnumerator.new(self, __method__) unless block_given?

    loop do
      @d += 2
      @n += @d  #    had to be reordered b/c
      yield @n  # <- yield has to come last
    end
  end
end
e = NumberSquarer.new.each
#=> #<ObjectEnumerator: #<NumberSquarer:0x00007fde60915e10 @d=-1, @n=0>:each>

e.next #=> 1
e.next #=> 4

other = enum.clone
#=> #<ObjectEnumerator: #<NumberSquarer:0x00007fcf23842520 @d=3, @n=4>:each>

enum.next #=> 9
enum.next #=> 16

other.next #=> 9