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