Ruby Marshal.load没有保持有序集

时间:2017-01-23 21:25:06

标签: ruby marshalling sortedset

我使用Marshal.dump将SortedSet对象保存在文件中。 集合中的元素也是对象(包括Comparable并实现< =>方法)。

稍后在使用Marshal.load恢复该对象时,从文件加载的SortedSet未排序...

知道为什么或如何解决它?

以下是重现问题的简化示例:

require 'set'
class Foo
  include Comparable

  attr_accessor :num

  def initialize(num)
    @num = num
  end

  def <=>(other)
    num <=> other.num
  end
end

f1 = Foo.new(1)
f2 = Foo.new(2)
f3 = Foo.new(3)

s = SortedSet.new([f2, f1, f3])

File.open('set_test.dump', 'wb') { |f| Marshal.dump(s, f) }

比,从我使用的文件中加载对象 -

File.open('set_test.dump', 'rb') { |f| ls = Marshal.load(f) }

**我使用Rails 3.2.3和Ruby 2.1.8

**从文件加载转储时 - 在新的/单独的rails控制台中执行(并且不要忘记复制粘贴Foo类的定义:-))

1 个答案:

答案 0 :(得分:4)

重现错误

我可以在我试过的每一个Ruby上重现这种行为。

# write_sorted_set.rb
require 'set'
class Foo
  include Comparable

  attr_accessor :num

  def initialize(num)
    @num = num
  end

  def <=>(other)
    num <=> other.num
  end
end

f1 = Foo.new(1)
f2 = Foo.new(2)
f3 = Foo.new(3)

s = SortedSet.new([f2, f1, f3])
File.open('set_test.dump', 'wb') { |f| Marshal.dump(s, f) }
p s.to_a

# load_sorted_set.rb
require 'set'
class Foo
  include Comparable

  attr_accessor :num

  def initialize(num)
    @num = num
  end

  def <=>(other)
    num <=> other.num
  end
end

ls = Marshal.load(File.binread('set_test.dump'))
p ls.to_a

启动时

ruby write_sorted_set.rb && ruby load_sorted_set.rb

输出

[#<Foo:0x000000010cae30 @num=1>, #<Foo:0x000000010cae08 @num=2>, #<Foo:0x000000010cadb8 @num=3>]
[#<Foo:0x0000000089be08 @num=2>, #<Foo:0x0000000089bd18 @num=1>, #<Foo:0x0000000089bc78 @num=3>]

为什么?

不使用可比较

使用此定义:

class Foo
  attr_accessor :num
  def initialize(num)
    @num = num
  end
end
load_sorted_set.rb中的

应引发异常(comparison of Foo with Foo failed (ArgumentError)),但事实并非如此。看起来SortedSet

未正确初始化Marshal.load

LIB / set.rb

查看SortedSet的{​​{3}}:

  module_eval {
    # a hack to shut up warning
    alias old_init initialize
  }

      module_eval {
        # a hack to shut up warning
        remove_method :old_init
      }

      @@setup = true
    end
  end

  def initialize(*args, &block) # :nodoc:
    SortedSet.setup
    initialize(*args, &block)
  end
end

看起来SortedSet已修补,以确保在SortedSet.setup 初始化之前执行SortedSet

Marshal.load似乎并不知道这一点。

解决方案

SortedSet.setup

你可以打电话

SortedSet.setup
require 'set'之后和Marshal.load

之前

SortedSet.new

您可以强制SortedSet初始化:

ls = SortedSet.new(Marshal.load(File.binread('set_test.dump')))