traitで優性の法則を表現する

Blog Single

ノーベル賞の発表が10月に入って続々とされております。
個人的にそういった分野が好きで調べていると、専門用語を目にして懐かしさを感じます。
以前も同じきっかけで記事を書きました。
そうだ、画像をバイナリ分解しよう

今回もこのシリーズでございます。
最近発想が変な方向へ暴走している気がして自分が心配です←

さて今回は久々に出会ったのはこちら。

「優性の法則」

このワードを聞いて、また私の脳内連想ゲームが始まり、
コードで優性の法則に基づく遺伝を表現したい、と。
そして思いついたのは

PHPのtraitで優性の法則を表現しよう

もはや意味がわかりませんが今回もお付き合いくださいませ←

前提

ほとんどの方はご存知だと思いますが、そもそも優性の法則とtraitとは?という方は以下を読んでからお進みください。

基本条件

今回の基本条件として、
猫の毛色を決定する遺伝子W(白色)と遺伝子B(黒色)を使う優性の法則を表現することにします。
もし知らない方は以下などを参考にしてみてください。

traitで表現する

さっそく書いていきましょう。

const GENE_WHITE = 'W';
const GENE_BLACK = 'B';

class GeneOperator
{
    /**
     * ランダムにどちらかの遺伝子を選択
     *
     * @param array $genes
     * @return string
     */
    public static function selectGene($genes)
    {
        return $genes[array_rand($genes)];
    }

    /**
     * 毛色が黒色かチェック
     *
     * @param string $genoType
     * @return boolean
     */
    public static function isBlack($genoType)
    {
        if (strpos($genoType, GENE_WHITE) !== false) {
            return false;
        }
        return true;
    }
}

まずは遺伝子の定数定義と、遺伝子操作用のクラスGeneOperatorを用意します。
GeneOperatorのisBlackメソッドでは、遺伝子Wがあると必ず白色になるという前提を元に設定しています。
これで共通処理に必要なものは揃いました。

trait WhiteParent
{
    protected $wpGene1 = GENE_WHITE;
    protected $wpGene2 = GENE_WHITE;

    public function getGene()
    {
        return GeneOperator::selectGene([$this->wpGene1, $this->wpGene2]);
    }
}

trait BlackParent
{
    protected $bpGene1 = GENE_BLACK;
    protected $bpGene2 = GENE_BLACK;

    public function getGene()
    {
        return GeneOperator::selectGene([$this->bpGene1, $this->bpGene2]);
    }
}

trait HybridChild
{
    use WhiteParent, BlackParent {
        WhiteParent::getGene as getWhiteParentGene;
        BlackParent::getGene as getBlackParentGene;
    }

    protected $hcGene1 = '';
    protected $hcGene2 = '';

    public function init()
    {
        $this->hcGene1 = $this->getWhiteParentGene();
        $this->hcGene2 = $this->getBlackParentGene();
    }

    public function getGene()
    {
        return GeneOperator::selectGene([$this->hcGene1, $this->hcGene2]);
    }
}

続いて純系の白猫WhiteParentと黒猫BlackParentのtraitと、その子供HybridChildのtraitを作成します。
純系=2つとも同じ遺伝子を持っているのでgetGene()でランダムに遺伝子を取り出しても白ならW、黒ならBが取り出されます。
よってHybridChildはWとBを1つずつ持つ雑種になります。
HybridChildでは2つのtraitのメソッド名が衝突しているので、メソッド名を違うものに定義して使用します。

class HybridGrandChild
{
    use HybridChild {
        getGene as getParent1Gene;
        getGene as getParent2Gene;
    }

    protected $hgcGene1 = '';
    protected $hgcGene2 = '';

    public function __construct()
    {
        $this->init();
        $gene1 = $this->getParent1Gene();
        $gene2 = $this->getParent2Gene();
        $this->hgcGene1 = $gene1 == GENE_WHITE ? $gene1 : $gene2;
        $this->hgcGene2 = $gene1 != GENE_WHITE ? $gene1 : $gene2;
    }

    public function getGene()
    {
        return GeneOperator::selectGene([$this->hgcGene1, $this->hgcGene2]);
    }

    public function getGenoType()
    {
        return $this->hgcGene1 . $this->hgcGene2;
    }
}

最後に、先ほどの雑種HybridChild同士から生まれる孫HybridGrandChildのclassを作ります。
こちらもHybridChildと同様に、衝突するメソッドを予め別名で定義しておきます。
この時、メソッド名変更の定義は一度だけでもいいのですが、
父親と母親とを区別した方が遺伝と意味合いを近づけることができるかなと思い、
あえて別名で2つメソッドを定義させました。
インスタンス化する際に、init()で親の遺伝子を決定させ、それを基に自分の遺伝子も決定。

さて以上のコードで生まれた猫の毛色はどうなるのか。
HybridGrandChildが100匹生まれた場合を想定して、実行した結果はこちらです!

// 出力用
$total = 0;
$black = 0;
$white = 0;
while ($total < 100) {
    $hybridGrandChild = new HybridGrandChild();
    $genoType = $hybridGrandChild->getGenoType();
    $isBlack = GeneOperator::isBlack($genoType);
    $black += $isBlack ? 1 : 0;
    $white += !$isBlack ? 1 : 0;
    $total++;
}

echo "100匹中、黒猫は{$black}匹でした。\n";

おまけ

ちなみに、確実に黒猫を出現させるにはどうするか。
猫の遺伝子そのものに手を加えないとすれば、
遺伝子を取り出すGeneOperator::selectGeneで確実に黒色を取り出せるように出来たらいいですね。

trait BlackGeneSelector
{
    /**
     * 遺伝子選択
     *
     * @param array $genes
     * @return string
     */
    public static function selectGene($genes)
    {
        if (in_array(GENE_BLACK, $genes)) {
            return GENE_BLACK;
        }
        return $genes[array_rand($genes)];
    }
}

class GeneOperator
{
    use BlackGeneSelector;

    // selectGeneを削除
}

最後に

もはやメインが遺伝の話になってしまって、あまり技術的に役に立つ情報はないかもしれませんが…
traitを使う機会が少ない方は、このコードをベースにどんどんtraitで書き換えても面白いかもしれませんね。
今回も最後まで私の脳内ワールドにお付き合いくださり、ありがとうございます!

参考

Posted by Mao Miyaji
千葉にある夢の国を愛して止まない、元「魚のお姉さん」のエンジニア。PHP, TypeScriptメインで、暇さえあれば色々な言語を一かじり。

Other Posts: