Rubyチュートリアル 〜英文小説の最頻出ワードを見つけよう!(その6)

さてこれまでに得た知識を基礎として
目的のRubyスクリプトを作ります
最初にベースとなるコードを提示して
これを少しずつ改良していきながらRubyを学びます


まずは手元にRubyで処理できる
英文小説のテキストファイルを用意します
以下のサイトがよさそうです


Project Gutenberg


ここから気に入った小説を
plain text形式で2、3ダウンロードします


もしrubyインタプリタが手元にないか
ヴァージョンが古いのなら
Ruby公式サイトにアクセスして入手します
4.8.10があればいいですが1.8.7でも1.9.1でも足ります
ターミナルを開いて ruby -v と打てばインストールされている
rubyのヴァージョンが分かります


さて準備が整ったら
まずは入力と出力をイメージしましょう
このRubyスクリプトの名前をtopwords.rbとします
もちろんtop10.rbでもtopoftheworld.rbでもかまいません

 $ ruby topwords.rb novel1.txt novel2.txt novel3.txt
 {'this' => 123, 'is' => 85, 'a' => 65, 'ruby' => 30, ... }


rubyインタプリタスクリプト名と
上で入手したファイルを渡します
その実行結果として
最頻出単語とその出現数のリストが得られる
そんなイメージがよさそうです


次にRubyスクリプトの大ざっぱなプランを描きます


例えば次のように

  • コマンド引数として渡したファイルをスクリプトに取り込むためにARGFというオブジェクトを使う
  • ARGFから順次ファイルの行を読み出す
  • 読み出した行から単語を切り出す
  • ハッシュオブジェクトを用意して単語とその出現数の対を格納する
  • ハッシュオブジェクトの内容をその出現数の順位で並べ替える
  • その上位30を取り出す

Version01

突然ですが
この方針による最初のスクリプトは次のようになりました

 dic = Hash.new(0)
 while line = ARGF.gets
   line.downcase!
   while line.sub!(/[a-z]+/, "")
     word = $&
     dic[word] += 1
   end
 end
 
 p dic.sort { |a, b| b[1] <=> a[1] }[0...30]


最初に単語と出現数のリストを格納する
ハッシュオブジェクトを用意します
ハッシュは通常 dic = {}で簡単に生成できますが
ここでは対応するキーがない場合の
デフォルト値0を設定するために
newメソッドを呼んでいます
これにより6行目の増分が可能になります


Rubyスクリプトに指定した引数をファイル名とみなして
その内容を持ったARGFというオブジェクトを作り出します
ARGFオブジェクトは
その内容にアクセスするためのメソッド群を持っており
ここではその1つであるgetsメソッドを使って
ファイルの各行を文字列オブジェクトとして得ています


ARGFといわれてもピンと来ませんが
何かの略です
たぶんAiR GolFかARGument Filesです


いえ
わかりました
ARt GarFunkelの略です
そのうちPRSMというのも出てくると思います


以下のサイトで「ARGF」を検索しリンクを辿れば
ARGFが持っているメソッドを調べられます


Ruby 1.9.2 Methods List


whileループで順次ファイルの行を変数lineに読み込みます
getsメソッドはファイルの終わりに来るとnilを返しますから
ここでループが終わります
読み込まれた行はdowncase!メソッドで小文字に変換され
次にsub!メソッドでそこから単語を切り出します


sub!メソッドは第1引数の正規表現の条件にマッチしたものを
第2引数に(ここでは空文字)置き換えます
sub!メソッドは元のline文字列オブジェクト自体を変更します
つまりlineはマッチするたびに短くなっていき
最後にはマッチするものが無くなってnilが返りループが終わります
マッチした単語はその都度変数$&でアクセスできます


取得したwordでハッシュdicのキーにアクセスし
対応するバリューを増分します
dicに対応wordが無い場合
デフォルト値0で項目が作成され1増分されます


次にハッシュオブジェクトであるdicをソートします
sortメソッドは
ハッシュの[key, value]を要素とする配列の配列を作り
ブロックの条件でこれをソートした結果を返します
ここではvalue値の大小でソートします
降順ソートとするためa,bを逆に書きます


最後に[]メソッドに0...30の範囲オブジェクトを渡して
対象の配列オブジェクトのみを取り出します
ドットが3つであることに注意してください
この場合は30つまり31番目の要素は範囲外になります


では実際に入手したファイルでこのスクリプトを実行してみましょう

 $ ruby topwords.rb 11.txt 1342.txt 18503.txt 
 [["the", 16077], ["of", 9823], ["and", 7482], ["to", 7098], ["in", 4456], ["a", 3841], ["that", 3161], ["was", 3040], ["it", 2919], ["i", 2881], ["her", 2550], ["she", 2313], ["as", 2134], ["you", 2071], ["not", 2057], ["be", 2044], ["is", 2033], ["his", 2009], ["he", 1940], ["for", 1927], ["with", 1875], ["on", 1638], ["had", 1567], ["but", 1519], ["s", 1495], ["all", 1363], ["at", 1344], ["by", 1308], ["this", 1249], ["have", 1201]]


うまくいきました
'the'が英文小説における最頻出ワードであることが分かりました
上の正規表現は「'」にうまく対応していないので
完全ではありませんが
一応これで仕事が片づきました
上司に報告が必要な人は
この結果をプリントアウトしてください


(次回に続く)