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.
In addition to @tadman's answer, the reason why works in 1.9.3
and not in 2.0.0
is because 2 years ago the decision was made to freeze the Fixnums (and Bignums) as evidenced by this and this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With