昔書いたコードを書き直す。(Simpson係数を求める)

昔書いたコードを直したくなったので直してみました。><

以前書いたコード

http://d.hatena.ne.jp/trotr/20071008#1191852704
そういえばこの頃は"if __FILE__ == $0"の意味を理解していなかった><。

問題点

  • 何回もpubmedに読みに行っている。
  • 変に複雑
  • 読みにくい(というか、後で読む気がしない)

あと、hpricotを使って取得した方が早いかもしれないとか思いました。

変更後のコード

変更点は以下のような感じです。。

  • classを作った。
  • Simsonモジュールを作った。
    • calcメソッドを定義したクラスを作れば、pubmedに限らずSimpson係数を求められる
  • exit(-1)のような形ではなく例外をだすようにした。
    • 前に作った時は例外とかよく分かってなかったような気がする。
  • 一度調べた値は覚えておくことにした。
    • Simsonモジュールの機能

速度の比較

"CDK2", "CDK6", "CDK3", "CDK5"で実行した時の値。
simpson.rbが以前のものでsimpson2.rbが書き直したものです。

$ time ruby1.8 simpson.rb
["CDK2+CDK6", 0.420595533498759]
["CDK2+CDK3", 0.673469387755102]
["CDK2+CDK5", 0.122]
["CDK6+CDK3", 0.183673469387755]
["CDK6+CDK5", 0.0347394540942928]
["CDK3+CDK5", 0.285714285714286]
ruby1.8 simpson2.tmp.rb  0.50s user 0.09s system 7% cpu 8.379 total

$ time ruby1.8 simpson2.rb
CDK2+CDK6 : 0.420595533498759
CDK2+CDK3 : 0.673469387755102
CDK2+CDK5 : 0.122
CDK6+CDK3 : 0.183673469387755
CDK6+CDK5 : 0.0347394540942928
CDK3+CDK5 : 0.285714285714286
ruby1.8 simpson2.rb  0.57s user 0.05s system 12% cpu 4.978 total

早くなっている。たぶん要素の数が増えるほど差は広がると思う。

書き直したコード

#!/usr/bin/env ruby1.9
# -*- encoding: utf-8 -*-
require 'open-uri'
require 'rexml/document'

module Simpson
  def initialize
    #puts "Simpson is included"
    @mem = { } # !> `&' interpreted as argument prefix
    @queue = []
    @compound_name = proc{ |x,y| "#{x}+#{y}"}
    @output = proc { |name,n| puts("#{name} : #{n}")}
  end
  
  def count
    raise "count methods is not defined."
  end
  
  def push *elements
    @queue.push(*elements)
  end
  
  def make_combination!(queue)
    result = []
    until queue.empty?
      x = queue.shift
      result.push *queue.map{ |y| [x,y]}
    end
    result
  end

  def run! &block
    calc_allcombination(@queue, &block)
  end
  
  def run &block
    calc_allcombination(@queue.dup, &block)
  end
  
  def calc_allcombination(queue, &block)
    block = @output unless block
    a = make_combination!(queue)
    a.each do  |x,y| 
      name = @compound_name[x,y]
      block.call(name, calc(x,y))
    end
  end

  def calc x,y
     get(@compound_name[x,y]) / [x,y].map{ |e| get(e)}.min
  end

  def set(gene,num)
    raise "value is zero" if num < 0
    @mem[gene] = num ;  return num
  end

  def get gene
   @mem[gene]? @mem[gene] : set(gene,count(gene))
  end
  public :run, :run!, :push, :calc
  private :count, :set, :get, :calc_allcombination
end

class Checker
  include Simpson
  def initialize
    super
    @base = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term="
  end 
  
  def prepare gene
    url = @base + gene
    open(url).read
  end

  def count gene
    source =  prepare gene
    doc = REXML::Document.new source
    doc.elements['/eSearchResult/Count'].text.to_f
  end
  private :prepare, :count
end

# ch = Checker.new
# ch.push("CDK2", "CDK6", "CDK3", "CDK5")
# ch.run!

# >> CDK2+CDK6 : 0.420595533498759
# >> CDK2+CDK3 : 0.673469387755102
# >> CDK2+CDK5 : 0.122
# >> CDK6+CDK3 : 0.183673469387755
# >> CDK6+CDK5 : 0.0347394540942928
# >> CDK3+CDK5 : 0.285714285714286