Diary

Diary?

学生の研究日記だったらしいです。多分。

開発日記。

オススメの本(頂いた本):

いちばんあたらしいの2015 5/29 5:41

_29(Fri)

色々行き詰まっているので、誰得な新機能を入れました。Rubyのしくみ読者用。

例えば、嘘偽りの無い ancestors が欲しい時はこうします。

require 'objspace'

def ObjectSpace.internal_ancestors_of klass
  ancestors = []
  while klass
    ancestors << klass
    klass = ObjectSpace.internal_super_of(klass)
  end
  ancestors
end

require 'pp'
pp String.ancestors
pp ObjectSpace.internal_ancestors_of(String)

結果。

[String, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]
[String,
 #<InternalObject:0x7c671c T_ICLASS>,
 Object,
 #<InternalObject:0x282eb58 T_ICLASS>,
 #<InternalObject:0x7c7568 T_ICLASS>,
 BasicObject]

ほうら、わかりやすい。

クラス / モジュールの参照図を書いてみるのも分かりやすい。

require 'objspace'
require 'pp'

def ObjectSpace.object_id_of obj
  if obj.kind_of?(ObjectSpace::InternalObjectWrapper)
    obj.internal_object_id
  else
    obj.object_id
  end
end

class Module
  def references
    h = {} # object_id -> [klass, class_of, super]
    stack = [self]
    while klass = stack.pop
      obj_id = ObjectSpace.object_id_of(klass)
      next if h.has_key?(obj_id)
      cls = ObjectSpace.internal_class_of(klass)
      sup = ObjectSpace.internal_super_of(klass)
      stack << cls if cls
      stack << sup if sup
      h[obj_id] = [klass, cls, sup].map{|e| (defined?(e.name) && e.name) ? e.name : e.inspect}
    end
    h.values
  end
end

pp String.references
[["String", "#<Class:String>", "#<InternalObject:0x806718 T_ICLASS>"],
 ["#<InternalObject:0x806718 T_ICLASS>", "Comparable", "Object"],
 ["Object", "#<Class:Object>", "#<InternalObject:0x271dbc4 T_ICLASS>"],
 ["#<InternalObject:0x271dbc4 T_ICLASS>",
  "PP::ObjectMixin",
  "#<InternalObject:0x807564 T_ICLASS>"],
 ["#<InternalObject:0x807564 T_ICLASS>", "Kernel", "BasicObject"],
 ["BasicObject", "#<Class:BasicObject>", "nil"],
 ["#<Class:BasicObject>", "#<Class:#<Class:BasicObject>>", "Class"],
 ["Class", "#<Class:Class>", "Module"],
 ["Module", "#<Class:Module>", "Object"],
 ["#<Class:Module>", "#<Class:#<Class:Module>>", "#<Class:Object>"],
 ["#<Class:Object>", "#<Class:#<Class:Object>>", "#<Class:BasicObject>"],
 ["#<Class:#<Class:Object>>",
  "#<Class:#<Class:Class>>",
  "#<Class:#<Class:BasicObject>>"],
 ["#<Class:#<Class:BasicObject>>",
  "#<Class:#<Class:Class>>",
  "#<Class:Class>"],
 ["#<Class:Class>", "#<Class:#<Class:Class>>", "#<Class:Module>"],
 ["#<Class:#<Class:Class>>",
  "#<Class:#<Class:Class>>",
  "#<Class:#<Class:Module>>"],
 ["#<Class:#<Class:Module>>",
  "#<Class:#<Class:Class>>",
  "#<Class:#<Class:Object>>"],
 ["Kernel", "#<Class:Kernel>", "nil"],
 ["#<Class:Kernel>", "Class", "Module"],
 ["PP::ObjectMixin", "Module", "nil"],
 ["Comparable", "Module", "nil"],
 ["#<Class:String>", "#<Class:Class>", "#<Class:Object>"]]

この配列の各要素は m, k, s の配列になっており、m のクラスは k、m の super は s という関係になっています。

で、これはグラフなので、graphviz に食わせるようにしてみると、

require 'objspace'
require 'pp'

def ObjectSpace.object_id_of obj
  if obj.kind_of?(ObjectSpace::InternalObjectWrapper)
    obj.internal_object_id
  else
    obj.object_id
  end
end

class Module
  def references
    h = {} # object_id -> [klass, class_of, super]
    stack = [self]
    while klass = stack.pop
      obj_id = ObjectSpace.object_id_of(klass)
      next if h.has_key?(obj_id)
      cls = ObjectSpace.internal_class_of(klass)
      sup = ObjectSpace.internal_super_of(klass)
      stack << cls if cls
      stack << sup if sup
      h[obj_id] = [klass, cls, sup].map{|e| (defined?(e.name) && e.name) ? e.name : e.inspect}
    end
    h.values
  end
end

class C
end

rank_set = {}

puts "digraph mod_h {"
C.references.each{|(m, s, k)|
  # next if /singleton/ =~ m
  puts "#{m.dump} -> #{s.dump} [label=\"super\"];"
  puts "#{m.dump} -> #{k.dump} [label=\"klass\"];"

  unless rank = rank_set[m]
    rank = rank_set[m] = 0
  end
  unless rank_set[s]
    rank_set[s] = rank + 1
  end
  unless rank_set[k]
    rank_set[k] = rank
  end
}

rs = [] # [[mods...], ...]
rank_set.each{|m, r|
  rs[r] = [] unless rs[r]
  rs[r] << m
}

rs.each{|ms|
  puts "{rank = same; #{ms.map{|m| m.dump}.join(", ")}};"
}

puts "}"

こんな絵が出ます。

http://www.atdot.net/fp_store/f.z1u2pn/file.g.png

ちょっと複雑なものを食わせてみると、

module M0; end
module M1; end
class C0; end
class C1 < C0
  include M0
  prepend M1
end

http://www.atdot.net/fp_store/f.wfu2pn/file.g.png

凄いコトに。

方向を弄ってみたら、

http://www.atdot.net/fp_store/f.9nu2pn/file.g.png

ちょっと見やすくなったかな。


k と s の順番間違えてた。

http://www.atdot.net/fp_store/f.ksu2pn/file.g.png

Log

2002 01 02 03 04 05 06 07 08 09 10 11 12
2003 01 02 03 04 05 06 07 08 09 10 11 12
2004 01 02 03 04 05 06 07 08 09 10 11 12
2005 01 02 03 04 05 06 07 08 09 10 11 12
2006 01 02 03 04 05 06 07 08 09 10 11 12
2007 01 02 03 04 05 06 07 08 09 10 11 12
2008 01 02 03 04 05 06 07 08 09 10 11 12
2009 01 02 03 04 05 06 07 08 09 10 11 12
2010 01 02 03 04 05 06 07 08 09 10 11 12
2011 01 02 03 04 05 06 07 08 09 10 11 12
2012 01 02 03 04 05 06 07 08 09 10 11 12
2013 01 02 03 04 05 06 07 08 09 10 11 12
2014 01 02 03 04 05 06 07 08 09 10 11 12
2015 01 02 03 04 05 06 07 08 09 10 11 12

SASADA Koichi (ko1 at atdot dot net) / Skype ID: ko1_ssd


rss