ちなみに

火曜日の空は僕を押しつぶした。

Ruby/Rails勉強会@関西 #24

http://jp.rubyist.net/?KansaiWorkshop24

行ってきましたRuby勉強会。2回目。

意気揚々と向かうも盛大に迷う。若さをアピールってわけでもないんですが、なんとなく歩いてみたら、曲がらなければいけないところで曲がらず、まっすぐ行ってしまい、人に聞いたら行き過ぎと言われて笑われました。

今回内容はRubyとかRailsもろって話はあまりなく、多方面の知識が得られたのが良かったかなと思います。RESTとかFlexの話は名前は知っているけどって状態だったのでおおまかにでも知識を得る事が出来たので良かったですし、iQubeの話はシステムの開発がどんな風に行われているのか知らなかったのでおもしろかったです。デモが見えなかったのが残念ですが。

そして、おまちかね初級者向けレッスン。

今回こそは全問解くぞと意気込んでいたのですが、やっぱり駄目。
自分がいかにあいまいな知識しか持ってないか思い知らされました。

解いたので、さらします。

第一問、英文の解析

文字数、単語数、行数、文字別の出現頻度、単語の出現頻度をカウントします。

puts `wc text.txt`

1行で書けました。すばらしい。(出現頻度無視)

・・・冗談です。ほんとはこっち。

count = {
  :line => 0,
  :char => 0,
  :word => 0,
}

frequency = {
  :linefreq => Hash.new {|h, k| h[k] = 0 },
  :wordfreq => Hash.new {|h, k| h[k] = 0 },
}

count[:line] = IO.readlines("text.txt").each {|line|
  count[:char] += line.split(//).each {|c|
    frequency[:linefreq][c] += 1 if c =~ /\w/
  }.length
  count[:word] += line.split(' ').each {|w|
    frequency[:wordfreq][w] += 1 if w =~ /\A\w+\Z/
  }.length
}.length

count.each {|k, v| puts "#{k}: #{v}" }
frequency.each do |k, v|
  puts "------------------\n" + 
       "#{k}\n" +
       "------------------"
  v.sort.each do |k, v|
    puts "#{k}: #{v}"
  end
end

なんだかかぶってる箇所が多くてもうちょういDRYでスマートなコードにしたいんですが、今の限界。
出力が面倒くさかったので、countとfrequencyで分けて集計してます。

第二問、ログの解析

汎用的に書こうとして挫折した感じ。
Macユーザもあきらめました。
英語が間違ってそうで心配。

#追記:Livedoor FeedFetcherをsplit(' ')してる。これは誤差です。

require 'date'

class LogAnalyser
  LOG = /\A([^\s]+)\s([^\s]+)\s([^\s]+)\s\[(.+)\]\s\"([^"]+)\"\s((?:\d+|-)\s(?:\d+|-))\s\"([^"]*)\"\s\"([^"]*)\"/

  def initialize(file = 'access.log')
    @log = File.open(file, 'rb').map {|line|
      LOG =~ line
      [$1, $2, $3, $4, $5.to_s.split(' '), $6, $7, $8].flatten
    }
  end

  def count_for(type, keyword)
    begin
      @log.inject(0) {|r, v|
        r += 1 if v[number_of(type)] =~ Regexp.new(keyword); r
      }
    rescue
      $stderr << 'error'
      exit
    end
  end

  def best_access_wday
    week = ['Sunday',
            'Monday',
            'Tuesday'
            'Wednesday',
            'Thursday',
            'Friday',
            'Saturday']
    rank = Hash.new {|h, k| h[k] = 0 }
    get_date_list.each {|date| rank[date.wday] += 1 }
    week[rank.max {|a, b| a[1] <=> b[1] }[0]]
  end

  # for browser only
  def ranking(type)
    return unless type == :browser
    count(type).sort_by {|k, v| -v }[0..9]
  end

  private
  def count(type)
    begin
      rank = Hash.new {|h, k| h[k] = 0 }
      @log.each do |l|
        b = l[number_of(type)].split(' ')[0].split('(')[0].split(';')[0]
        rank[b] += 1
      end
      rank
    rescue
      $stderr << 'error'
      exit
    end
  end

  def get_date_list
    @log.map {|l|
      Date.parse l[number_of(:date)]
    }
  end

  def number_of(type)
    case type
    when :ipaddress ; 0
    when :user      ; 2
    when :date      ; 3
    when :method    ; 4
    when :uri       ; 5
    when :protocol  ; 6
    when :status    ; 7
    when :referer   ; 8
    when :browser   ; 9
    else raise
    end
  end
end

lana = LogAnalyser.new
puts "Access for 'index.php': #{lana.count_for(:uri, 'index.php')}"
puts "First access day by Mac user: I can't analyse..."
puts "Access from Google: #{lana.count_for(:referer, 'google')}"
puts "Best acsess day of week: #{lana.best_access_wday}"
puts "Top 10 browsers:"
lana.ranking(:browser).each_with_index do |(name, count), i|
  puts "#{(i+1).to_s.rjust(2)} #{name.ljust(22)}: #{count}"
end