我有以下代码:
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它输出一个错误,并显示:
> 4.divisors
/home/cygnus/Projects/project-euler/stuff/numbers.rb:24:in `divisors_sum': can't modify frozen Fixnum (RuntimeError)当我删除实例变量@divisors,@divisors_sum...中的缓存时,错误消失了。这只发生在ruby 2.0上。在1.9.3上运行,没有问题。发生什么事了呢?
发布于 2013-04-06 02:30:35
@divisors是Fixnum实例上的一个实例变量,因此您正在尝试修改它。你可能不应该这么做。
那这个呢?
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这意味着Numeric中的方法不修改任何实例,缓存存储在其他地方。
发布于 2013-04-06 02:42:50
除了@tadman的回答之外,为什么在1.9.3中工作而不是在2.0.0中工作的原因是因为2年前决定冻结Fixnums (和Bignums),this和this证明了这一点。
发布于 2013-04-06 03:36:50
正如其他人指出的那样,ruby核心已经决定Fixnums和Bignums现在是冻结的,所以你不能在这些类的对象中设置实例变量。
一种解决方法是创建一个外部模块,该模块保留由这些冻结对象的值索引的散列缓存,并使用这些散列的元素而不是实例变量:
require 'prime'
module FrozenCacher
def FrozenCacher.fcache
@frozen_cache ||= {}
end
def fcache
FrozenCacher.fcache[self] ||= {}
end
end
class Numeric
include FrozenCacher
#... math helpers
def divisors
return [self] if self == 1
fcache[: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
fcache[:divisors_sum] ||= divisors.reduce(:+)
end
#... more methods that evaluate code and 'caches' and assigns (||=) to instance variables
end
puts 4.divisors.inspect # => [1, 2]
puts FrozenCacher.fcache.inspect # => {4=>{:divisors=>[1, 2]}}
puts 10.divisors.inspect # => [1, 5, 2]
puts FrozenCacher.fcache.inspect # => {4=>{:divisors=>[1, 2]}, 10=>{:divisors=>[1, 5, 2]}}https://stackoverflow.com/questions/15840233
复制相似问题