Ruby/Rails勉強会@関西 #24に行ってきたよ!

卒論もおわり時間に余裕もできたので,Ruby/Rails勉強会@関西 #24にいってまいりました.スタッフおよび参加者のみなさん,おつかれさまでした!今回は,いつもと少し毛色の違った内容(開発環境, 開発現場,Flexのはなし)でなかなか興味深かったですね.

さて,今回は某氏に,最後のセッションのRuby初心者レッスンで書いたコードを,そのまま晒すべしとの指令うけているので,そのまま晒したいと思います.変数名などがいい加減ですが,1bitも変更してはいけないらしいので,気にしないでください.

第一問 テキスト解析

テキストを読み込み,以下の数を調べる.

  1. 文字数
  2. 単語数
  3. 行数
  4. 文字列の出現頻度
  5. 単語別出現頻度

ただし,対象のテキストはtext.txtというファイルに保存されています.

準備

ひとつひとつの問題ごとにスクリプトを書くのは面倒なので,irbで実行します.ただし,irbをまちがえて終了したときに,なんどもファイルをオープンするのは面倒なので,以下のようなスクリプトを用意して,txt.rbとして保存しておきます.

def s
  str = ""
  open 'text.txt' do |file|
    str = file.read
  end
  str
end

このあとirbを,

$ irb -r txt.rb

とrオプションとともに起動すれば,sがirb中で使えます.これでまちがってirbを終了しちゃっても,このsで対象のテキストがすぐに参照できて楽ちんですね.
ちなみに対象の,text.txtはokkezさんが配ってくれた物ではなくて,自分で適当に用意した以下の文章です.

I entirely abandoned the study of letters.
Resolving to seek no knowledge other than that of which could be found in myself or else in the great book of the world, I spent the rest of my youth travelling, visiting courts and armies, mixing with people of diverse temperaments and ranks, gathering various experiences, testing myself in the situations which fortune offered me, and at all times reflecting upon whatever came my way so as to derive some profit from it.
(Descartes, Discourse on the Method)

というわけで,以下のようにたんたんとirbで問題を解いていきました.

1,文字数
>> s.length
=> 506
2. 単語数
>> s.split.length
=> 86
3. 行数
>> s.split(/\n/).length
=> 3
4. 文字別の出現頻度
>> s.split(//).inject({}) { |h, k| h[k].nil? ? h[k] = 1 : h[k] += 1; h}
=> {"k"=>4, "v"=>7, " "=>84, ","=>7, 
# 長すぎるので略
"u"=>10}

ちょっとinjectが使いたくて.三項演算子よりもcoolな書き方がありそうですね.

5. 単語別出現頻度
>> s.split.inject({}) { |h, k| h[k].nil? ? h[k] = 1 : h[k] += 1; h}
=> {"with"=>1, "Resolving"=>1, "ranks,"=>1, 
# 長すぎるので略
 "no"=>1}
おれおれ添削

ここまでがレッスンの時間中に書いたそのままのコードです.今回は,初級者レッスンでTAをやっていたので,okkezさんが説明してくれているときに,こっそり書きました.なので,かなりいい加減かもしれません(ココいいわけ).

そこで,レッスンが終わった後で気づいたポイントを以下のように修正してみました.

まず,はじめに定義したsという関数ですが,内部でstrという変数に読み込んだ値を代入しています.これはちょっと無駄で,

def s
  open 'text.txt' do |file|
    file.read
  end
end

とすれば,良かったですね.openのブロックの返値がそのまま関数の返値として返ります.

ちなみに,関数sを定義しているtxt.rbで

@s = open('text.txt') {|f| f.read }

とすれば,@sでテキストの内容が参照できるわけなんですが,irbのコンテキストで@を使うのがなんかいやだったので,関数にしています.

コード中では,関数sを何度も使っています.このsは呼ばれるたびに,IOをたたくのでかなり遅いはずです.なので,以下のように,sによって読み込んだ文字列を,別の変数に代入してから使った方が良いですね.

>> str = s
>> str.length

問題4,5についてですが,三項演算子がちょっといやな感じです.調べてみるとHashのデフォルト値が指定できるらしいので,問題4の場合,

>> s.split(//).inject(Hash.new(0)) { |h, k| h[k] += 1; h}

でOKなようです.かなり,短くなった!

問題4,5は結果の出力がいまいちですね.ちゃんと出現頻度でsortしてから出力してみました.

>> s.split(//).inject(Hash.new(0)) { |h, k| h[k] += 1; h} .sort {|a, b| b[1] <=> a[1]} .each do |e|
?>   puts "'#{e[0]}': #{e[1]}"
>> end
' ': 84
'e': 57
't': 40
'o': 33
's': 28
# 以下略

さすがに,ここまでやるとメソッドチェインが長すぎて若干キモイですね.つうか,Hashのsortなどはじめて知ってびっくりした.PerlだとHashのkeyだけsortしているところ.Rubyは直感的だなぁ.

第二問 ログ解析

Apacheのログを解析して以下を調べる.

だったのですが,TAをやっていて時間がなくて(ry.
明日あたりに試しにやってみてブログにあげますよ.

まとめ

ほとんど,初級者レッスンの内容になってしまいました.今回は発表タイトルにNetBeansスライド一枚分しか発表されなかった方もいるので大丈夫でしょう.

某氏におかれましては,このコードご覧くださいますようお願いいたしします.

あまり難しい内容ではないのですが,もっと短く書けるぜとか,そもそもなんかおかしいぜというような指摘がございましたら是非指摘をお願いします.

というわけで,Ruby勉強会#24に行って参りました.今回は,就職などで関西を出て行ってしまう方も多かったみたいです.中には,ずいぶんお世話になった人もいます.これまでどうもありがとうございました.新天地でもがんばってくださいね!