这里有一些关于Enumerable::take
方法的内容。我的代码应该生成一个n
大小的连续素数列表。
使用next
方法时效果很好,而take(n)
则返回无法解释的内容。这是代码:
编辑 shift
方法似乎是这里的问题(见下文)。我仍然不明白为什么,但
require 'prime'
def consecutives(size)
Enumerator.new do |enum|
ps = Prime.lazy # all primes numbers
a = Prime.take(size) # itinialize the array
size.times{ps.next} # skip the first <size> values from ps
loop do
enum << a # return result
a << ps.next # add next prime number
a.shift # remove first item from a
end
end
end
c3 = consecutives(3) #create an iterator for arrays of size 3
现在使用next
可以正常工作:
3.times { p c3.next} #=> [[2,3,5],[3,5,7],[5,7,11]]
而take
会返回一些我根本无法获得的东西:
p c3.take(3) #=> [[5, 7, 11], [5, 7, 11], [5, 7, 11]]
对这里发生的事情有所了解吗?
编辑将a << ps.next ; a.shift
替换为a = (a+ [ps.next])[-1..1]
确实解决了问题,并且枚举器的行为符合预期。
我仍然不知道这里发生了什么,所以我想问题仍然存在。
答案 0 :(得分:1)
问题:
enum << a # return result
这里产生一个数组。或者你认为。在引擎盖下,传递引用到数组。然后你继续修改你认为是你内部的数组。实际上,这些修改会影响两个数组,因为内存中只有一个数组,并且有两个数组引用它。最后,您获得相同的一个数组和4个引用(一个内部和三个引用)。
您找到的解决方法之所以有效,是因为您创建了新数组。只需拨打.dup
即可。
require 'prime'
def consecutives(size)
Enumerator.new do |enum|
ps = Prime.lazy
a = Prime.take(size)
size.times{ps.next}
loop do
enum << a.dup # <== here
a << ps.next
a.shift
end
end
end
c3 = consecutives(3)
c3.take(3) # => [[2, 3, 5], [3, 5, 7], [5, 7, 11]]