プログラミング言語/Ruby/コードリーディング/しりとりゲーム
module Rands def bool_rand rand(2).zero? end module_function :bool_rand def array_rand(array) array[rand(array.size)] end module_function :array_rand def char_rand (rand(?z - ?a) + ?a).chr end module_function :char_rand end
モジュールの記述の中にメソッドの定義が並んでいるが、 その前に書かれている一行「module_function」に言及しておく。
通常、モジュール内の実装はあるクラスにincludeして始めて意味を持つ。
module Vip def ok print "おk" end end
というモジュールがあり、例えばStringクラスにincludeされると、
class String include Vip end str = "abc" str.ok #=> おk
と、Stringクラスの実装のように呼べる。
さて、JavaがMathクラスにstaticメソッドばかりを定義して Math.sqrt(2)のような使い方を提供しているように、メソッドの「公式集」 のようなもの作る必要も出てくる。
Javaはクラスしかないんだから仕方ないのだが、 Mathクラスからインスタンスを生成する必要はないはずだ。が、Rubyには インスタンスを作らないクラスのようなもの=モジュールがあるのでそれを使う。
そして、やっとmodule_functionの話に行き着くが、これはモジュールのメソッドを Vip.okのような形でも呼べるように自動でしてくれるメソッドだ。 (このようなメソッドをモジュール関数と呼んだりする)
上のように引数なしで一行に書くとそれ以降のメソッド全てが対象に、
module_function :ok
のようにメソッド名のSymbolを渡すとそのメソッドを対象に作業を行う。 (privateやpublicなどの可視性設定も似たような感じ)
module Rands def bool_rand def array_rand(array) def char_rand
上で長々と語ったとおり、module_functionのおかげで
Rands.bool_rand Rands.array_rand(arr) Rands.char_rand
という形で使えるようになった。
そもそもモジュールにまとめなければ、 bool_randはトップレベルでのみ、 array_randはAutoPlayerでだけ、 char_randはManualPlayerだけでしか使用されていないのだから こんな手間もいらないのだが、 乱数系のメソッドが「公式集」としてひとつのものに集まっているという 美しさのほうを取ることにした。
def bool_rand rand(2).zero? end
rand()はKernelモジュールに定義され、KernelはObjectクラスにincludeされている。 つまり関数的なメソッドである。 rands(n)で、0~n-1の間の整数をランダムに生成する。
rands(2)が出すのは0か1かだけなわけで、その結果から呼ばれているメソッド Fixnum#zero?は、自身がゼロかどうかを返す。
つまり、このメソッドは(理想的には)半々の確立で真か偽かどちらかを返す。
def array_rand(array) array[rand(array.size)] end
rand(array.size)は0~array.size-1の範囲の乱数生成だ。そしてこの範囲は まさにarrayのインデックスの範囲である。
というわけで、これは引数の配列の要素をひとつランダムに返す。
def char_rand (rand(?z - ?a) + ?a).chr end
?aとか?zはRubyのリテラルの一種で、?cなら'c'という文字の文字コードを返す。 `文字コード'というものの実体は数値なのでRubyではFixnumである。そして、 Fixnum#chrというメソッドで"c"などの一文字を現すStringを返す。
つまり、rand(?z - ?a)は文字コードの'a'~'z'の幅で乱数を生成し、そこに?aを 足すことで文字コード'a'~'z'のいずれかをランダムに得て、Stringにして返す。