I have the following code:
require 'prime'
class Numeric
#... math helpers
def divisors
return [self] if self == 1
@divisors ||= prime_division.map do |n,p|
(0..p).map { |i| n**i }
end.inject([1]) do |a,f|
a.product(f)
end.map { |f| f.flatten.reduce(:*) } - [self]
end
def divisors_sum
@divisors_sum ||= divisors.reduce(:+)
end
#... more methods that evaluate code and 'caches' and assigns (||=) to instance variables
end
Which outputs an error with:
> 4.divisors
/home/cygnus/Projects/project-euler/stuff/numbers.rb:24:in `divisors_sum': can't modify frozen Fixnum (RuntimeError)
The error disappears when I remove caching into the instance variables @divisors
, @divisors_sum
... etc. And this only happens on ruby 2.0. Ran it on 1.9.3 without issues. What's happening?
@divisors
is an instance variable on an instance of Fixnum, so you are trying to alter it. You probably shouldn't be doing this.
What about this?
module Divisors
def self.for(number)
@divisors ||= { }
@divisors[number] ||= begin
case (number)
when 1
[ number ]
else
prime_division.map do |n,p|
(0..p).map { |i| n**i }
end.inject([1]) do |a,f|
a.product(f)
end.map { |f| f.flatten.reduce(:*) } - [ number ]
end
end
end
def self.sum(number)
@divisors_sum ||= { }
@divisors_sum[number] ||= divisors(number).reduce(:+)
end
end
class Numeric
#... math helpers
def divisors
Divisors.for(self)
end
def divisors_sum
Divisors.sum(self)
end
end
This means that the methods in Numeric do not modify any instance, the cache is stored elsewhere.