LoginSignup
4
3

More than 5 years have passed since last update.

殺人事件の解決までを Exception で再現してみる feat. ポートピア連続殺人事件

Last updated at Posted at 2019-02-28

これは何?

  • 昔の有名な殺人事件を、Exception の try~catch で再現してみます。
  • Exceptionの握りつぶしについて「完全に握りつぶす」「1つ以外を握りつぶす」「一切握りつぶさない」の3パターンを使って、徐々に事件の解決に近づきます。
  • ソースコード(portpia.php)は最後に全部掲載します。

はじまり

事件発生!
山川社長が何者かに殺害された。
刑事のヤスが事件解決の為、捜査を開始した。

山川社長、文江、ヤスを呼び出す。

new でオブジェクトを作ります。ここではクラス定義は秘密にしておきます。(最後に掲載します)

$objs = [];
$objs[] = new yamakawa();
$objs[] = new fumie();
$objs[] = new yasu();

聴取1回目:自己紹介させます。

Exceptionに重要発言を持たせています。ここでは、自己紹介するだけなので、Exceptionを完全に握りつぶします。

foreach ($objs as $obj) {
    print "\n";
    try {
        $obj->listening();
    } catch (Exception $e) {
        // 吐かないよう握りつぶす
        ;
    }
}

実行結果

山川(殺害された人)   >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!

文江(山川の秘書)     >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!

ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!

聴取2回目:事情を聴いてみます。

重要発言(ようはException)を1つだけ聴いてみます。

foreach ($objs as $obj) {
    print "\n";
    try {
        $obj->listening();
    } catch (Exception $e) {
        // 1つだけ吐かせる
        $obj->say($e->getMessage());
    }
}

実行結果

山川(殺害された人)   >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
山川(殺害された人)   >>> 実は、昔、詐欺をやっていたという過去があります

文江(山川の秘書)     >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
文江(山川の秘書)     >>> 実は、ヤスは私の兄です

ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
ヤス(捜査している人) >>> 実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります

聴取3回目:真実を吐かせてみる

全ての重要発言、ようは全てのExceptionを聴いてみます。ネストしたExceptionを全て展開しているだけです。

コード

foreach ($objs as $obj) {
    print "\n";
    try {
        $obj->listening();
    } catch (Exception $e) {
        // 全部吐かせる
        do {
            $obj->say($e->getMessage());
        } while($e = $e->getPrevious());
    }
}

実行結果

山川(殺害された人)   >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
山川(殺害された人)   >>> 実は、昔、詐欺をやっていたという過去があります
山川(殺害された人)   >>> 実は・・・後悔しています。罪滅ぼしのため文江を秘書に雇いました

文江(山川の秘書)     >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
文江(山川の秘書)     >>> 実は、ヤスは私の兄です
文江(山川の秘書)     >>> 実は・・・兄の犯罪に加担していました

ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
ヤス(捜査している人) >>> 実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります
ヤス(捜査している人) >>> 実は・・・詐欺の黒幕は山川社長と知り、復讐のため私が殺害しました

聴取の総括:どこで吐いたのか

事件は解決したんですが、ソースコード中のどこで重要発言があったのか(つまりExceptionが発生したのか)全部出力してみます。

foreach ($objs as $obj) {
    print "\n";
    try {
        $obj->listening();
    } catch (Exception $e) {
        // どこで吐いたのかも含めて、全部吐かせる
        // print $e."\n"; でいいのだけど、出力が見づらいので行頭を少し加工
        print preg_replace('/^/m', '  |', $e)."\n";
    }
}

実行結果

山川(殺害された人)   >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
  |Exception: 実は・・・後悔しています。罪滅ぼしのため文江を秘書に雇いました in /home/ubuntu/20190227/portopia.php:31
  |Stack trace:
  |#0 /home/ubuntu/20190227/portopia.php(23): yamakawa->deepListening()
  |#1 /home/ubuntu/20190227/portopia.php(137): yamakawa->listening()
  |#2 {main}
  |
  |Next Exception: 実は、昔、詐欺をやっていたという過去があります in /home/ubuntu/20190227/portopia.php:25
  |Stack trace:
  |#0 /home/ubuntu/20190227/portopia.php(137): yamakawa->listening()
  |#1 {main}
:
(以降長いので省略、最後に全部載せてます)

まとめ

  • 犯人はヤス
  • 重要発言、つまり Exception は聞き漏らさないようにしましょう。
    • catch(Exception $e){ ; } すると、重要発言は何も聞けない。
    • 重要発言を聞くために catch(Exception $e){ print $e->getMessage(); } としても、1つしか聞けない。
    • 重要発言を聞き漏らさない為には catch(Exception $e){ print $e; } とする。
  • PHP版で力尽きました。もし気に入ってもらえたなら、ほかの言語でも事件を解決してみてください:wink:

添付:全ソースコード

portpia.php
<?php

abstract class investigation
{
    abstract function whatName();
    abstract function listening();
    function say($msg) {
        print $this->whatName() . " >>> " . $msg . "\n";
    }
}

class yamakawa extends investigation
{
    function whatName()
    {
        return '山川(殺害された人)  ';
    }

    function listening()
    {
        $this->say("社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!");
        try {
            $this->deepListening();
        } catch (Exception $e) {
            throw new Exception("実は、昔、詐欺をやっていたという過去があります", 0, $e);
        }
    }

    function deepListening()
    {
        throw new Exception("実は・・・後悔しています。罪滅ぼしのため文江を秘書に雇いました");
    }
}

class fumie extends investigation
{
    function whatName()
    {
        return '文江(山川の秘書)    ';
    }

    function listening()
    {
        $this->say("山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!");
        try {
            $this->deepListening();
        } catch (Exception $e) {
            throw new Exception("実は、ヤスは私の兄です", 0, $e);
        }
    }

    function deepListening()
    {
        throw new Exception("実は・・・兄の犯罪に加担していました");
    }
}

class yasu extends investigation
{
    function whatName()
    {
        return 'ヤス(捜査している人)';
    }

    function listening()
    {
        $this->say("山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!");
        try {
            $this->deepListening();
        } catch (Exception $e) {
            throw new Exception("実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります", 0, $e);
        }
    }

    function deepListening()
    {
        throw new Exception("実は・・・詐欺の黒幕は山川社長と知り、復讐のため私が殺害しました");
    }
}

$objs = [];
$objs[] = new yamakawa();
$objs[] = new fumie();
$objs[] = new yasu();

print "\n";
print "***\n";
print "*** 聴取1回目:自己紹介(Exceptionを完全に握りつぶす)\n";
print "***\n";
foreach ($objs as $obj) {
    print "\n";
    try {
        $obj->listening();
    } catch (Exception $e) {
        // 吐かないよう握りつぶす
        ;
    }
}

print "\n";
print "***\n";
print "*** 聴取2回目:事情を聴く(直近のExceptionのメッセージ1つだけ吐かせる)\n";
print "***\n";
foreach ($objs as $obj) {
    print "\n";
    try {
        $obj->listening();
    } catch (Exception $e) {
        // 1つだけ吐かせる
        $obj->say($e->getMessage());
    }
}

print "\n";
print "***\n";
print "*** 聴取3回目:真実を吐かせる(ネストしているExceptionのメッセージも含め全部吐かせる)\n";
print "***\n";
foreach ($objs as $obj) {
    print "\n";
    try {
        $obj->listening();
    } catch (Exception $e) {
        // 全部吐かせる
        do {
            $obj->say($e->getMessage());
        } while($e = $e->getPrevious());
    }
}

print "\n";
print "***\n";
print "*** 聴取の総括:どこで吐いたのか(Exceptionのメッセージ以外の情報も全部吐かせる)\n";
print "***\n";
foreach ($objs as $obj) {
    print "\n";
    try {
        $obj->listening();
    } catch (Exception $e) {
        // どこで吐いたのかも含めて、全部吐かせる
        // print $e."\n"; でいいのだけど、出力が見づらいので行頭を少し加工
        print preg_replace('/^/m', '  |', $e)."\n";
    }
}

添付:全実行結果

PHP7.2で動作確認しましたが、他のバージョンでも動くと思います。

$ php --version
PHP 7.2.15-0ubuntu0.18.04.1 (cli) (built: Feb  8 2019 14:54:22) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.15-0ubuntu0.18.04.1, Copyright (c) 1999-2018, by Zend Technologies

$ php portopia.php 

***
*** 聴取1回目:自己紹介(Exceptionを完全に握りつぶす)
***

山川(殺害された人)   >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!

文江(山川の秘書)     >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!

ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!

***
*** 聴取2回目:事情を聴く(直近のExceptionのメッセージ1つだけ吐かせる)
***

山川(殺害された人)   >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
山川(殺害された人)   >>> 実は、昔、詐欺をやっていたという過去があります

文江(山川の秘書)     >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
文江(山川の秘書)     >>> 実は、ヤスは私の兄です

ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
ヤス(捜査している人) >>> 実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります

***
*** 聴取3回目:真実を吐かせる(ネストしているExceptionのメッセージも含め全部吐かせる)
***

山川(殺害された人)   >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
山川(殺害された人)   >>> 実は、昔、詐欺をやっていたという過去があります
山川(殺害された人)   >>> 実は・・・後悔しています。罪滅ぼしのため文江を秘書に雇いました

文江(山川の秘書)     >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
文江(山川の秘書)     >>> 実は、ヤスは私の兄です
文江(山川の秘書)     >>> 実は・・・兄の犯罪に加担していました

ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
ヤス(捜査している人) >>> 実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります
ヤス(捜査している人) >>> 実は・・・詐欺の黒幕は山川社長と知り、復讐のため私が殺害しました

***
*** 聴取の総括:どこで吐いたのか(Exceptionのメッセージ以外の情報も全部吐かせる)
***

山川(殺害された人)   >>> 社長やってます。文江を秘書に雇ってます。ついさっき誰かに殺されました!
  |Exception: 実は・・・後悔しています。罪滅ぼしのため文江を秘書に雇いました in /home/ubuntu/20190227/portopia.php:31
  |Stack trace:
  |#0 /home/ubuntu/20190227/portopia.php(23): yamakawa->deepListening()
  |#1 /home/ubuntu/20190227/portopia.php(137): yamakawa->listening()
  |#2 {main}
  |
  |Next Exception: 実は、昔、詐欺をやっていたという過去があります in /home/ubuntu/20190227/portopia.php:25
  |Stack trace:
  |#0 /home/ubuntu/20190227/portopia.php(137): yamakawa->listening()
  |#1 {main}

文江(山川の秘書)     >>> 山川社長の秘書です。社長の遺体の第一発見者は私と、社長の甥の川村さんです!
  |Exception: 実は・・・兄の犯罪に加担していました in /home/ubuntu/20190227/portopia.php:54
  |Stack trace:
  |#0 /home/ubuntu/20190227/portopia.php(46): fumie->deepListening()
  |#1 /home/ubuntu/20190227/portopia.php(137): fumie->listening()
  |#2 {main}
  |
  |Next Exception: 実は、ヤスは私の兄です in /home/ubuntu/20190227/portopia.php:48
  |Stack trace:
  |#0 /home/ubuntu/20190227/portopia.php(137): fumie->listening()
  |#1 {main}

ヤス(捜査している人) >>> 山川社長殺害事件の捜査やってます。社長と揉めていた、山川の甥の川村が怪しい!
  |Exception: 実は・・・詐欺の黒幕は山川社長と知り、復讐のため私が殺害しました in /home/ubuntu/20190227/portopia.php:77
  |Stack trace:
  |#0 /home/ubuntu/20190227/portopia.php(69): yasu->deepListening()
  |#1 /home/ubuntu/20190227/portopia.php(137): yasu->listening()
  |#2 {main}
  |
  |Next Exception: 実は、親の会社が詐欺にあい倒産。両親自殺。妹と生き別れの過去があります in /home/ubuntu/20190227/portopia.php:71
  |Stack trace:
  |#0 /home/ubuntu/20190227/portopia.php(137): yasu->listening()
  |#1 {main}
4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3