ふと gem mirror をしたところ、spec.4.8 というファイルが目についた。
一瞬 gemspec が入っているのかな、と思ったが、中身を覗いてみると名前、バージョン、プラットフォームだけだった。
% ruby -rpp -e 'pp Marshal.load(File.binread("specs.4.8"))'
[["_", Gem::Version.new("1.0"), "ruby"],
["_", Gem::Version.new("1.1"), "ruby"],
["_", Gem::Version.new("1.2"), "ruby"],
["-", Gem::Version.new("1"), "ruby"],
["0mq", Gem::Version.new("0.1.0"), "ruby"],
["0mq", Gem::Version.new("0.1.1"), "ruby"],
["0mq", Gem::Version.new("0.1.2"), "ruby"],
["0mq", Gem::Version.new("0.2.0"), "ruby"],
["0mq", Gem::Version.new("0.2.1"), "ruby"],
["0mq", Gem::Version.new("0.3.0"), "ruby"],
...
これで何かできるだろうかと思って、とりあえず最初の数の分布を調べてみた。(同じ名前のパッケージはいちばん大きなバージョン以外は無視)
% ruby -rpp -e '
Marshal.load(File.binread("specs.4.8")).
group_by {|ary| ary[0] }.
each {|k, vs|
v = vs.map {|ary| ary[1] }.max
puts v.to_s[/\d+/].to_i
}
'|sort -n |uniq -c
68581 0
15280 1
2943 2
945 3
332 4
82 5
46 6
27 7
13 8
26 9
17 10
12 11
3 12
7 13
7 14
2 15
2 19
2 20
2 21
1 22
1 26
1 28
2 30
1 35
5 42
1 99
1 100
1 111
1 969
1 971
1 1987
2 2004
1 2006
2 2008
22 2009
6 2010
19 2011
15 2012
26 2013
35 2014
1 3000
1 7159
2 9000
1 9001
1 50000
1 20120125
1 20120701
1 20121026
1 2009022403
1 2010072900
バージョン0 が多いというのはたしかにそうだな。
グラフを描いてみよう。
ふつうに描くと Y軸、X軸にはりついてしまうのだが、さりとて 0 があるから対数グラフには向かないしどうしたものか、と思いつつとりあえず ggplot2 で対数グラフにしたら 0 は印がはみ出る感じに描かれるのだな。

両対数グラフで、バージョン10あたりまでは直線的に落ちていく感じ。
gem mirror が終わったので、手元にはたくさん gem がある。具体的には 477367個、137GB
とりあえずどんなサイズの gem があるだろうかということでグラフにしてみた。

10KB くらいが多い。
GitHub: rubygems: Don't define MirrorCommand if already defined.
GitHub: milkode: grenfiletest.rb:12:in `match': invalid byte sequence in UTF-8 (ArgumentError)
どのようなメソッドのチェーンが多いだろうか。つまり、obj.m1.m2 というような呼び出しで、m1 と m2 の組み合わせは何が多いだろうか。
というわけで調べてみた。Ripper.sexp で構文木を取り出して、そのような式をてきとうに (完璧を目指さずに) 取り出すのは以下のように簡単にできる。
% cat extract-call-seq.rb
#!/usr/bin/ruby
# usage:
# extract-call-seq.rb file.rb ...
require 'ripper'
def method_call_name(tree)
if tree.is_a?(Array) &&
tree[0] == :call &&
tree[3].is_a?(Array) &&
tree[3][0] == :@ident &&
tree[3][1].is_a?(String)
n = tree[3][1]
n.sub!(/\A\s*\./, '') # [ruby-dev:48684] [Bug #10411]
if /\A[a-z_A-Z][a-z_A-Z0-9]*[!?]?\z/ !~ n
warn "unexpected method name: #{n.inspect}"
end
n
else
nil
end
end
def check(tree)
meth2 = method_call_name(tree)
if meth2
meth1 = method_call_name(tree[1])
if meth1
puts "#{meth1} #{meth2}"
end
end
end
def extract(tree)
return unless tree.is_a? Array
check(tree)
tree.each {|elt|
extract(elt)
}
end
def main
STDERR.puts ARGV.inspect
ARGV.each {|fn|
STDERR.puts fn.inspect
extract(Ripper.sexp(File.read(fn)))
}
end
main
各 gem の最新版を展開してあるので、そこにある *.rb なファイル全部に適用して集計すると上位100個は以下のようになる。いくつかコメントもつけてみた。
367550 u normalize なんか機械生成されたコードみたい 27444 size should rspec の類ですかね 25358 class name クラスの名前を使うのはよくわかる 20018 name should rspec 16083 should be rspec 15603 to_s should rspec 13568 length should rspec 13178 body should rspec 11638 count should rspec 多すぎ 11395 to_s gsub 10983 now to_i Time.now.to_i だとすると、これは使ってほしくないんだけどなぁ 9990 class to_s 9241 class should rspec 8884 should not rspec 8600 to_s split 8479 keys each Hash なら each_key もあるけど、破壊的変更が必要なときに hash.keys.each を使うこともあるのはわかる 8295 to_s downcase 7918 compact join 7744 interval end 7657 class new 7579 status should 7495 endpoint const_get 6863 to_s upcase 6667 value should 6590 once with 6584 last is_a? 引数の最後が Hash かどうかの検査とか? 6538 now utc 6429 keys sort 6222 backtrace join 6164 routes draw 6116 sort each 5900 should raise 5884 any_instance expects 5791 name to_s 5611 id should 5597 first should 5560 to_s strip 5481 first name 5340 errors on 5313 message should 4917 to_i to_s 整数の文字列ってこんなに使うのだな 4831 flatten compact 4704 first is_a? 4699 new tap 4642 sort should 4349 any_instance stub 4339 should equal 4338 downcase to_sym 4315 keys first 4270 now to_f 4251 keys include? 4229 now strftime 4005 type should 4001 text should 3968 elements each 3952 title should 3865 any_instance stubs 3811 flatten each 3766 to_s sub 3756 root join 3743 id to_s 3716 application config 3665 be ok 3646 sort join 3642 values first 3466 first id 3454 application routes 3424 to_s empty? 3414 children first 3326 once and_return 3313 to_s camelize 3304 path should 3269 sql should 3268 first to_s 3266 keys map 3062 to_s underscore 3049 to_a should 3030 be nil 2987 errors full_messages 2986 name underscore 2970 strip empty? 2963 children each 2914 errors add 2904 name split 2838 sqls should 2830 name to_sym 2824 url should 2800 flatten map 2794 to_s capitalize 2793 values each 2793 class send 2784 flatten uniq 2780 logger debug 2779 should match 2762 any_instance should_receive 2740 string should 2738 all should 2722 keys join 2694 all each 2671 not be
うんまぁ rspec は多いですね。
そういえば、all_symbols がどう使われているのかという話が前に (開発者会議で) あったな。
24 all_symbols count 10 all_symbols map 8 all_symbols collect 4 all_symbols size 3 all_symbols select 2 all_symbols sort_by 2 all_symbols length 1 all_symbols should 1 all_symbols is_a? 1 all_symbols clone 1 all_symbols all?
数を数えることが多いというのはわかる。
Hash.new はどのように使われているのか
% time gmilk -a 'Hash.new' |sed 's/^.*Hash.new */Hash.new /'|sort|uniq -c|sort -n|tail -100
Because number of records is large, Milkode use external tool. (Same as 'gmilk -e grep')
26 Hash.new { |h,k| h[k] = h.length }
26 Hash.new {|h, k| h[k] = [] }
26 Hash.new {|h, k| h[k] = {}}
26 Hash.new {|hash, key| hash[key] = lambda {|entry| CoercibleString.coerce(entry)}}
27 Hash.new (hash)
27 Hash.new (self, env, @default_options)
27 Hash.new { |hash, key|
27 Hash.new { |hash,key| hash[key] = {} }
28 Hash.new ) { |row| ... }
28 Hash.new { |h,k| h[k] = '' }
29 Hash.new ("ETag" => 'HELLO', "content-length" => '123')
29 Hash.new (*args)
29 Hash.new (0) }
29 Hash.new (false)) do |methods, attr|
29 Hash.new , pirate.changes
30 Hash.new ()
30 Hash.new , node.attributes
30 Hash.new { |h,k| h[k] = Mutex.new }
30 Hash.new }.should raise_error(ArgumentError)
31 Hash.new (header)
32 Hash.new (false).update(
32 Hash.new ;
33 Hash.new ) }
34 Hash.new ("default")
34 Hash.new (self, env)
35 Hash.new (0)]
35 Hash.new { |hash,key| hash[key] = [] }
36 Hash.new ({
36 Hash.new { |h, k|
37 Hash.new (0)) { |h,part| h[part.first] += part.last; h }
37 Hash.new ]
37 Hash.new { |h, k| h[k] = Array.new }
37 Hash.new {|h,base|
38 Hash.new { |h, k| h[k] = Set.new }
39 Hash.new (0.0)
39 Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
42 Hash.new { [] }
43 Hash.new { 0 }
43 Hash.new { |h,k| h[k] = 0 }
43 Hash.new { |h,pid| h[pid] = {} }
44 Hash.new (1)
44 Hash.new (self)
44 Hash.new do |hash, key|
44 Hash.new {|h,k| h[k] = 0}
46 Hash.new (nil)
46 Hash.new , &block)
46 Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
47 Hash.new ("foo" => ["bar", "baz"])
47 Hash.new (output)
48 Hash.new ("")
48 Hash.new {|hash, key| hash[key] = []}
50 Hash.new (Codepoint.new)
50 Hash.new .tap do |hash|
50 Hash.new Hash(getRuntime());
53 Hash.new ("Foo-Bar" => "baz")
54 Hash.new { |h,k|
56 Hash.new (default)
56 Hash.new do |h, table_name|
58 Hash.new ("Content-MD5" => "d5ff4e2a0 ...")
58 Hash.new unless defined?(@_cycles)
59 Hash.new (default).merge!(self)
60 Hash.new ([]))
60 Hash.new (default).merge(self)
63 Hash.new { |hash, key| hash[key] = {} }
65 Hash.new { |hash, key| hash[key] = Array.new }
66 Hash.new (
67 Hash.new (0)
67 Hash.new { |h, k| h[k] = 0 }
69 Hash.new { |h, k| h[k] = k.chr }
75 Hash.new { |h,k| h[k] = Array.new }
76 Hash.new {|h,k| h[k] = {}}
84 Hash.new ('')
95 Hash.new ({})
99 Hash.new { |h, k| h[k] = {} }
102 Hash.new {|h, k| h[k] = []}
103 Hash.new do |h, k|
108 Hash.new , params
119 Hash.new Hash(runtime);
121 Hash.new ,
126 Hash.new do |h,k|
133 Hash.new {|h,k| h[k] = [] }
136 Hash.new (false)
139 Hash.new 0
144 Hash.new ([])
160 Hash.new ("foo" => "bar")
162 Hash.new do |hash, region|
162 Hash.new do |region_hash, key|
261 Hash.new (headers)
262 Hash.new { |hash, key| hash[key] = [] }
285 Hash.new { |h,k| h[k] = {} }
308 Hash.new {|h,k| h[k] = []}
429 Hash.new ()
448 Hash.new { |h, k| h[k] = [] }
789 Hash.new { |h,k| h[k] = [] }
827 Hash.new
1015 Hash.new }
1038 Hash.new do |hash, key|
1220 Hash.new )
1672 Hash.new (0)
17878 Hash.new
gmilk -a 'Hash.new' 1.42s user 0.20s system 98% cpu 1.645 total
sed 's/^.*Hash.new */Hash.new /' 0.10s user 0.00s system 6% cpu 1.644 total
sort 0.14s user 0.00s system 8% cpu 1.782 total
uniq -c 0.00s user 0.01s system 0% cpu 1.782 total
sort -n 0.02s user 0.00s system 1% cpu 1.799 total
tail -100 0.00s user 0.00s system 0% cpu 1.798 total
Hash.new { |h,k| h[k] = [] } がとても多いな。
[latest]