奇怪的枚举器行为(副作用?)

时间:2014-01-23 14:00:22

标签: ruby

这里有一些关于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]确实解决了问题,并且枚举器的行为符合预期。 我仍然不知道这里发生了什么,所以我想问题仍然存在。

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]]