Rubyにおけるrand(乱数)の挙動について

Rubyのrandの挙動はややこしく、最近さらにややこしくなったのでメモ。

乱数メソッドの種類

Rubyには3種類のrandがある。

  • Kernel#rand (ふつうのrand)
  • Random.rand
  • Random#rand

ふるまい(before 1.9.2)

1.9.2までは、
Random#randは引数にInteger, Float, Rangeを扱うことができるのに対し、
Kernel#rand, Random.randではIntegerしか扱うことができない

rand 10                   #=> 7 
rand 7.5                  #=> 4 
rand 10..20               #=> TypeError: can't convert Range into Integer

Random.rand 10            #=> 8 
Random.rand 7.5           #=> 3 
Random.rand 10..20        #=> TypeError: can't convert Range into Integer

Random.new.rand 10        #=> 8 
Random.new.rand 7.5       #=> 6.258043599450456 
Random.new.rand 10..20    #=> 20

仕様変更の提案

この挙動が不満だったのでruby-listで聞いてみたところ、
redmineに投げてみたら」
と言われたので投げたら通ってしまった。


Random#randとKernel#randでRangeを扱えるように(http://redmine.ruby-lang.org/issues/4605)



提案ないようは最初はKernel#randとかでもFloatとRangeを扱えるようにするものだったが、それだとFloat値が与えられた時の互換性がなくなってしまいヤバい。
しょうがないのでRangeの対応だけを追加するものを提案した。

実装

提案したらsora_hとmrknさんが即効でパッチを書いてくれてcommitされたようです。

ソースコードながめてたらKernel#randとRandom.randはCレベルでは全く同じ関数と知ってびっくり

まとめ

Kernel#rand Random.rand Random#rand
1.9.2まで Integer Integer Integer, Float, Range
1.9.3から Integer, Range Integer, Range Integer, Float, Range

Random#randの存在を知らずに「RubyではFloatの乱数作れないのかよ……つかえねえ」とか思う人がでてこないといいなあ