Rubyチュートリアル 〜英文小説の最頻出ワードを見つけよう!(その11)
さてもう改良点はないでしょうか
スクリプト全体をもう一度みてみましょう
module Enumerable def take_by(nth) sort_by { |elem| yield elem }.take(nth) end end class Hash def top_by_value(nth, &blk) take_by_value(nth, opt(false), &blk) end def bottom_by_value(nth,&blk) take_by_value(nth, opt, &blk) end private def take_by_value(nth, sort_opt) select { |key, val| block_given? ? yield(val) : val }.take_by(nth) { |key, val| sort_opt[val] } end end WORDS = ARGF.read.downcase.scan(/[a-z]+/) DICTIONARY = WORDS.inject(Hash.new(0)) { |dic, word| dic[word] += 1 ; dic } p DICTIONARY.top_by_value(30)
3行目が思いの外すっきりしたので
1行目のメソッドチェーンが気になりだしました
ちょっと病的な感覚かもしれません
でも楽しいRubyの学習のために先に進みます
Version14
「添付ファイルから単語を取って配列に入れる」
という操作は汎用性がありそうです
今度はこれをいじりましょう
ARGFに対するtake_wordsメソッドを定義します
ARGFは通常のオブジェクトと違い
属するクラスを持っていません
ですから上で示したハッシュや配列のように
その属するHashクラスやArrayクラスにメソッドを定義する
といったことができません
ではどうするか
こういう場合はそのオブジェクト専用の
名無しクラスにメソッドを定義します
class << ARGF def take_words(regexp) read.downcase.scan(regexp) end end WORDS = ARGF.take_words(/[a-z]+/) DICTIONARY = WORDS.inject(Hash.new(0)) { |dic, word| dic[word] += 1 ; dic } p DICTIONARY.top_by_value(30)
この場合クラスに名前を与えずに
オブジェクトを<<で接ぎ木します
この無名クラスはSingletonクラス
または特異クラスなどと呼ばれます
クラスを定義しない
別の書き方もあります
def ARGF.take_words(regexp) read.downcase.scan(regexp) end
こう書いたときSingletonメソッド
または特異メソッドなどと呼ばれます
take_wordsには正規表現を渡せるようにしてます
先頭がx,y,zで始まる単語のみを対象に
最頻出ワード30をリストしてみましょう
WORDS = ARGF.take_words(/[xyz][a-z]+/) DICTIONARY = WORDS.inject(Hash.new(0)) { |dic, word| dic[word] += 1 ; dic } p DICTIONARY.top_by_value(30) #> [["you", 2071], ["zabeth", 636], ["your", 597], ["ys", 556], ["ying", 322], ["years", 226], ["yes", 214], ["ything", 176], ["ydia", 172], ["yet", 163], ["young", 144], ["xt", 143], ["ye", 137], ["year", 124], ["yself", 108], ["zzy", 97], ["yed", 82], ["ybody", 77], ["ylon", 75], ["zed", 67], ["ze", 64], ["yourself", 60], ["xpected", 58], ["yton", 58], ["yphon", 55], ["xactly", 54], ["yond", 54], ["xed", 52], ["yright", 48], ["yone", 45]]
Singletonメソッドについては以下が参考になるかもしれません
Rubyのクラスはオブジェクトの母、モジュールはベビーシッター - hp12c
メソッドが見つからないならRubyに作ってもらえばいいよ! - If method_missing, define_method by Ruby - - hp12c
ここまで来るともう止まりません
はっきり言って2行目も気になります
DICTIONARY = WORDS.inject(Hash.new(0)) { |dic, word| dic[word] += 1 ; dic }
Version15
しかも頻出ワード辞書というのは汎用性がありそうです
make_freq_dicメソッドとしてArrayに定義しましょう
ええこれは明らかに行き過ぎです
Arrayに定義されるべきメソッドは
あらゆる種類の配列で使われうるメソッドのみを定義すべきです
でももうわたしにも止められないのです!
class Array def make_freq_dic inject(Hash.new(0)) { |dic, word| dic[word] += 1 ; dic } end end WORDS = ARGF.take_words(/[a-z]+/) DICTIONARY = WORDS.make_freq_dic p DICTIONARY.top_by_value(30)
すっきりです
ARGFから単語を取り出しWORDSで参照する
WORDSから頻出ワードを作ってDICTIONARYで参照する
DICTIONARYから頻出トップ30を取って出力する
1つのオブジェクトに1つのメソッド
さすがにもう気が済みました
わたしの暴走を許してくださりありがとうございます
(次回に続く)