2014-01-23 5 views
1

Здесь есть что-то, что я не могу получить о методе 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

Проблема:

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

Да, вы абсолютно правы. Не в первый раз меня путают, думая, что ссылка является фактическим значением -_-. Благодаря ! – aherve

+0

@aherve: это мое удовольствие :) –

Смежные вопросы