さてこれまでに得た知識を基礎として、目的の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スクリプトの大ざっぱなプランを描きます。

例えば次のように、

  1. コマンド引数として渡したファイルをスクリプトに取り込むためにARGFというオブジェクトを使う
  2. ARGFから順次ファイルの行を読み出す
  3. 読み出した行から単語を切り出す
  4. ハッシュオブジェクトを用意して単語とその出現数の対を格納する
  5. ハッシュオブジェクトの内容をその出現数の順位で並べ替える
  6. その上位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’が英文小説における最頻出ワードであることが分かりました。上の正規表現は「’」にうまく対応していないので完全ではありませんが、一応これで仕事が片づきました。上司に報告が必要な人は、この結果をプリントアウトしてください。

(次回に続く)



blog comments powered by Disqus
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS