サン・トウカできのみを貰う処理を例にします。処理は以下の通りです。
[ クラボ, カゴ, モモン, チーゴ, ナナシ, ヒメリ, オレン, キー ]
これを、ライブラリをまったく使わずに書くと以下のようになります。
このような書き方はいくつか問題があります。すぐに思いつくだけでも以下のようなものがあります。
PokemonPRNGを使うと以下のように書けます。
(ここでもう力尽きた)
LCGの操作として頻出する以下の処理が提供されています。
seed
を1つ進める : Advance()
seed
を一度に複数進める : Advance(n)
seed
を1つ戻す : Back()
seed
を一度に複数戻す : Back(n)
seed
を得る(元の値は変更しない) : NextSeed()
seed
を得る(元の値は変更しない) : NextSeed(n)
seed
を得る(元の値は変更しない) : PrevSeed()
seed
を得る(元の値は変更しない) : PrevSeed(n)
seed
は1つ進む): GetRand()
m
で割った余りを取得する(seed
は1つ進む): GetRand(m)
0
からいくつ進めたseed
かを取得する : GetIndex()
initialSeed
からいくつ進めたseed
かを取得する : GetIndex(initialSeed)
Advance(n)
やGetIndex()
は効率的なアルゴリズムで実装されているため、処理速度の低下を気にすることなく使うことができます。
乱数処理を再利用可能にするために、部品(コンポーネント)化しましょう。
「乱数を使った生成処理をコンポーネント化したもの」を表すインタフェース IGeneratable
が用意されています。
これを利用する側は次のように書きます。
また、Advance
やGetRand
などに合わせて、seed
を主語にして書けるように、レシーバと引数を逆転させる拡張メソッドも用意されているため、以下のようにも書けます。
ホウエン地方には他にもランダムにきのみをくれるNPCがいます。他のNPCのバリエーションも作成してみましょう。
同じような処理が繰り返し現れています。重複を無くしましょう。
「ランダムに貰えるきのみの処理はほぼ定型だけど、きのみのラインナップを差し替えてバリエーションを作りたい」「きのみのラインナップは既知のデータとして提供したい」を両立するために継承を使っています。この程度なら穏当な継承の使い方と言えるでしょう(ただし継承を使わないと両立ができないわけではありません)。
IGeneratable
のほかにIGeneratableEffectful
、ILcgUser
、ILcgUtilizer
が用意されています。
IGeneratableEffectful
IGeneratable
は引数に受け取ったseed
を変更しないのに対し、IGeneratableEffectful
は受け取ったseed
を書き換えます。生成結果のリストを計算したいときはseed
を1つずつ進めながら生成結果を列挙したいため、seed
を書き換えないIGeneratable
を使うと良いでしょう。一方でコンポーネント化された乱数処理を内部的に使用するような場合はseed
も一緒に進める必要があります。そのような場合はIGeneratableEffectful
を使いましょう。
拡張メソッドはIGeneratable
と同様にseed.Generate(IGeneratableEffectful)
です。generator
のクラスが両方のインタフェースを実装している場合、seed.Generate(generator)
はIGeneratable
とIGeneratableEffectful
のどちらを対象とすれば確定できなくなりコンパイルが通らなくなるため、var generator = new HogeGenerator() as IGeneratable
のように、型をどちらかのインタフェースとして指定しておきましょう。
ILcgUser
「乱数処理の生成結果には興味が無く、消費を発生させる用途で使いたい」ようなものにはILcgUser
が使えます。こちらはseed
を主語とする拡張メソッドはUsed
という名前になっています。
ILcgUtilizer
ILcgUtilizer
は…IGeneratableEffectful
と同じ定義になってますね…。何らかの結果を生成するのが主作用で「乱数の消費」を副作用と見なしたい場合はIGeneratableEffectful
を、「乱数の消費」が主作用で、副作用として得られる生成結果も利用したい場合はILcgUtilizer
を使う…というような使い分けを想定していたような気がします。そのうちしれっと削除するかもしれません。。。
慣れると便利です。