• 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
 
 

rubyで普通にinjectを使って合計を求めようとした場合、こんな感じになります。

   1  (1..10).inject(0){|sum, i| sum + i} #=> 55

しかし、以下のように書く事も出来ます。

   1  (1..10).inject(&:+) #=> 55

posted by Png genki on Sun 10 May 2009 at 04:55

ポジティブ/ネガティブ投票による正しいランキング方法が以下の記事で紹介されています。

How Not To Sort By Average Rating

この計算方法では、投票数が少ない場合には分散が大きく不正確な評価で、 投票数が多くなるにつれて分散が小さく正確な評価が得られているという事を考慮しています。以下数式


\frac{\hat{p}+\frac{z^2_{\alpha/2}}{2N}\pm{}z_{\alpha/2}\sqrt{
\frac{\hat{p}(1-\hat{p})+\frac{z^2_{\alpha/2}}{4N}}{N}
}}{1+\frac{z^2_{\alpha/2}}{N}}

これはScoreの{}_{1-\alpha/2}信頼区間を表しています。 この信頼区間の下界をランキングのスコアにすれば良い事になります。 ここで、{}_{\hat{p}}は、

です。全体に占めるポジティブ投票数の割合ですね。

{}_{z_{\alpha/2}}は標準正規分布上の {}_{1-\alpha/2}信頼区間の有意確率です。

さて、五段階評価によるRatingに同様のテクニックを適用する場合はどうしたらいいでしょうか。 単純に、1回の投票を4度のpositive/negative投票行為だと考えると (例えば星1つは[-,-,-,-], 星3つは[+,+,-,-])、以下のような {}_{\hat{p}'}を使えば良さそうです。


\hat{p}'=\frac{\sum_{i\in{[1,5]}}(i-1)N_{i}}{4N}

従って、最終的にこんな感じになります。


\frac{\hat{p}'+\frac{z^2_{\alpha/2}}{2N}\pm{}z_{\alpha/2}\sqrt{
\frac{\hat{p}'(1-\hat{p}')+\frac{z^2_{\alpha/2}}{4N}}{N}
}}{1+\frac{z^2_{\alpha/2}}{N}}

Rubyコードで表すとこんな感じですね。

   1  require 'statistics2'
   2  
   3  def score(ni, alpha)
   4    n = ni.inject(&:+)
   5    return 0 if n == 0
   6    m = ni.size
   7    z = Statistics2.pnormaldist(1-alpha/2)
   8    phat = m.times.zip(ni).inject(0){|s,(i,j)| s + i*j}/((m - 1.0)*n)
   9    (phat + z*z/(2*n) - z*Math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
  10  end

posted by Png genki on Sun 10 May 2009 at 04:46

Enumerable#grepを使う事で、文字列の配列から、 所望のパターンにマッチするものだけを抜き出す事が出来ますが、 以下のようにブロックを指定する事で、 マッチした部分だけを取り出す事が出来ます。

   1  Dir["config/environments/*.rb"].grep(%r{(\w+)\.rb$}){$1}
   2  #=> ["development", "production", "rake", "staging", "test"]

痒いところに手が届く感じですね。

posted by Png genki on Thu 30 Apr 2009 at 14:32

例えばJavaScriptだと"\u3000"と書けば良かったのですが、少なくともRuby-1.8系ではこれに相当する記法が無さそうだったので、いつも困ってました。 色々調べてみた結果、以下のようにすれば良さそうです。

   1  [0x3000].pack("U") == " "  #=> true

長い事迂回していた問題だったのでスッキリしました :-)

Special Thanks

  • @yukiko pack("U")が使えそうという情報をありがとうございます。
posted by Png genki on Fri 24 Apr 2009 at 06:28

Rails勉強会#40のセッションでも言いましたが、 エラーが発生した時等に表示されるbacktraceの表示は、 コンソールのように上から下に流れる画面上では 通常の逆順のほうが追いやすいと思います。

ということで、ちょっと試しにそのような挙動にしてみました。 以下のようなコードを実行するとテストできます。

   1  class Exception
   2    def set_backtrace(bt)
   3      puts bt.reverse.map{|i|
   4        i.sub(/^([\/\.\w]+):(\d+)/,
   5          "\e[33m\\1\e[m:\e[32m\\2\e[m")
   6      }.join("\n")
   7    end
   8  end
   9  
  10  def foo
  11    raise StandardError
  12  end
  13  
  14  def bar
  15    foo
  16  end
  17  
  18  bar

こんな感じに、エラーが発生した場所に近い順に、 下から上にたどっていく感じに表示されます。

ss

上から下のbacktraceに目が慣れてしまっているので、最初は追いかけにくさを感じますが、一旦慣れてしまえば、スタックが深くて相当スクロールアップしなければエラーの発生源までたどり着けないという場合には良さそうな気がします。

stackという概念のイメージのせいで、新しいものが上の方に来るようになっているのだと思いますが、実用上は新しいものが下にくるこのスタイルの方が便利ではないでしょうか。

posted by Png genki on Thu 23 Apr 2009 at 14:27 with 2 comments

Rakeでnamespaceなどを使った場合に、Rubyのシンボルをネストさせたい時があるのですが、

   1  task :foo => :"bar:baz"

のように書くのが、DSL的にちょっとかっこうわるいです。 そこで、以下のようなものがあればちょっと奇麗に書けます。

   1  class Symbol
   2    def method_missing(method, *args, &block)
   3      if args.empty? && block.nil?
   4        [self, method].join(':').intern
   5      else    
   6        super 
   7      end
   8    end
   9  end
  10  
  11  >> :foo #=> :foo
  12  >> :foo.bar #=> :"foo:bar"
  13  >> :foo.bar.baz #=> :"foo:bar:baz"

いかがでしょう。

posted by Png genki on Wed 15 Apr 2009 at 05:49

GAE/Jにはファイル数制限(1000まで)があるので、なるべく不要なファイルは減らしたいのが人情というものです。 ということで、unpackしたgemsから不要なファイルを掃除するために、以下のようなスクリプトを使用しています。

script/strip_gems

   1  #!/usr/bin/env ruby
   2    
   3  Dir['gems/gems/*'].each do |dir|
   4    %w(
   5      Generaters TODO README.* README
   6      spec/**/* test/**/* examples/**/* tasks/**/*
   7      tutorial/**/* demo/**/*
   8      CHANGES CHANGES.txt CHANGELOG
   9      LICENSE MIT-LICENSE Rakefile CONTRIBUTORS
  10      Manifest.txt History.txt install.rb setup.rb
  11    ).each do |pattern|
  12      Dir[File.join(dir, pattern)].each do |path|
  13        command = "rm -rf #{path}"
  14        puts command 
  15        system command
  16      end
  17    end
  18  end

Gemの中に含まれているファイルには制約が無いので、 簡単なパターンで全てを掃除するのは難しいですね。

posted by Png genki on Wed 15 Apr 2009 at 04:54

gemを指定したディレクトリにインストールする方法を紹介します。 以下のように -i オプションでディレクトリを指定します。

   1  gem install -i ./gems data_objects --no-ri --no-rdoc

Gemをunpackしている場合に、もう一つ、二つGemを追加したい時などに重宝します。

posted by Png genki on Sun 12 Apr 2009 at 17:36 with 2 comments

Rails-2.3からRackに対応した事と関連が深いと思うのですが、 ruby-openidを使っていると、logger関係のエラーが出るようです。 ruby-openidでは、デフォルトでは以下のように

   1      @@logger = Logger.new(STDERR)

STDERRを使うようになっているのですが、これがRackと相性が悪いようです。 以下のように、config/initializers/*.rbあたりで別途loggerを設定すると良いようです。

   1  OpenID::Util::logger = Logger.new(File.join(Rails.root, %w(log openid.log)))

posted by Png genki on Wed 1 Apr 2009 at 21:18

何回か忘れたので、記憶を定着させるためにメモしておきます。 rubyでLOAD_PATHに特定のディレクトリを追加したい場合、以下のように RUBYLIB環境変数が利用できます。

   1  export RUBYLIB=/usr/lib/site_ruby:$RUBYLIB

posted by Png genki on Sun 29 Mar 2009 at 00:30