問題3.4

http://www.amazon.co.jp/dp/4839942390


ハノイの塔。懐かしい。
某国営放送N○Kの入社試験で出題された記憶。笑。
学生時代からテレビ無かったので、面接で好きなテレビ番組聞かれて答えられずに落ちました^ ^;

問題

古典的なハノイの塔の問題では、3つの塔とN枚のサイズの異なる円盤を用いて塔の間を移動させます。最初は円盤が下から上に向かって小さくなるように(どの円盤も自身より大きな円盤の上に乗っているように)なっています。そして以下のような制約を持ちます。


(1) 一度に1枚の円盤しか動かせない。
(2) 塔の一番上にある円盤を他の塔に移動させる。
(3) 円盤を置くときは、それ自身より大きなものの上にしか置けない。


スタックを用いて、最初の塔に積み上がっている円盤を最後の塔に移動させるプログラムを書いてください。


doctestって1行にすればfor文とか使えるんですね。これはいいことに気がついた。

#encoding: utf-8


class Tower():
    '''
    ハノイの塔を解くクラス。再帰使用。
    >>> DISK_NUM = 1
    >>> towers = []
    >>> for tower_num in range(0, 3): towers.append(Tower())
    >>> for disk_num in range(0, DISK_NUM): towers[0].add_disk(disk_num)
    >>> towers[0].move_disks(DISK_NUM, towers[2], towers[1])
    >>> towers[0].show_disks()
    >>> towers[2].show_disks()
    0
    >>> DISK_NUM = 2
    >>> towers = []
    >>> for tower_num in range(0, 3): towers.append(Tower())
    >>> for disk_num in range(0, DISK_NUM): towers[0].add_disk(disk_num)
    >>> towers[0].move_disks(DISK_NUM, towers[2], towers[1])
    >>> towers[0].show_disks()
    >>> towers[2].show_disks()
    0
    1
    >>> DISK_NUM = 3
    >>> towers = []
    >>> for tower_num in range(0, 3): towers.append(Tower())
    >>> for disk_num in range(0, DISK_NUM): towers[0].add_disk(disk_num)
    >>> towers[0].move_disks(DISK_NUM, towers[2], towers[1])
    >>> towers[0].show_disks()
    >>> towers[2].show_disks()
    0
    1
    2
    '''
    def __init__(self):
        # ディスク格納用スタック。
        self.disks_stack = []

    # タワーのトップのディスクをtarget_towerに移動。
    def _move_top(self, target_tower):
        target_tower.add_disk(self.disks_stack.pop())

    # diskをタワーに追加。
    def add_disk(self, disk):
        self.disks_stack.append(disk)

    # selfからtarget_towerへディスクを移動。buffer_towerを一時バッファとして使用。
    def move_disks(self, index, target_tower, buffer_tower):
        if index > 0:
            self.move_disks(index - 1, buffer_tower, target_tower)
            self._move_top(target_tower)
            buffer_tower.move_disks(index - 1, target_tower, self)
        else:
            pass

    # タワーのスタックに格納されたディスクの表示
    def show_disks(self):
        count = 0
        while len(self.disks_stack) > count:
            print self.disks_stack[count]
            count += 1


def _test():
    import doctest
    doctest.testmod()


if __name__ == '__main__':
    _test()

問題3.5

http://www.amazon.co.jp/dp/4839942390


頭の体操って感じ。老人のボケ防止に、これ位の簡単なアルゴリズムの問題っていいと思う。笑。

問題

MyQueueというクラス名で、2つのスタックを用いてキューを実装してください。

#encoding: utf-8


class MyQueue():
    '''
    2つのスタックで構成されるキュー。
    >>> my_queue = MyQueue()
    >>> my_queue.push(0)
    >>> my_queue.push(1)
    >>> my_queue.push(2)
    >>> my_queue.pop()
    0
    >>> my_queue.pop()
    1
    >>> my_queue.pop()
    2
    >>> my_queue.pop()
    >>> my_queue.push(999)
    >>> my_queue.pop()
    999
    '''
    def __init__(self):
        # pushされたらとりあえず格納するキュー
        self.push_queue = []
        # push_queueを逆順にしてpop_queueに格納して使用
        self.pop_queue = []

    def push(self, num):
        self.push_queue.append(num)

    def pop(self):
        # キューが空なら何もしない
        if len(self.push_queue) != 0 or len(self.pop_queue) != 0:
            # push_queueに格納したデータを逆順にして、pop_queueに格納する
            for counter in range(0, len(self.push_queue)):
                self.pop_queue.append(self.push_queue.pop())
            return self.pop_queue.pop()


def _test():
    import doctest
    doctest.testmod()


if __name__ == '__main__':
    _test()

第4章 問題の再現

http://www.amazon.co.jp/dp/4873115930/


デバッグするには、まず問題の再現が必要ってことを、3章より掘り下げて書いた章ですね。

目次

 4.1 デバッグの最初の作業
 4.2 問題環境の再現
 4.3 プログラム実行の再現
  4.3.1 データの再現
  4.3.2 ユーザの対話操作の再現
  4.3.3 コミュニケーションの再現
  4.3.4 時刻の再現
  4.3.5 乱数の再現
  4.3.6 動作環境の再現
  4.3.7 スケジューリングの再現
  4.3.8 物理現象の影響
  4.3.9 デバッグツールの影響
 4.4 システムとの対話の再現
 4.5 ユニットに注目する
  4.5.1 コントロール層の構成
  4.5.2 制御の例
  4.5.3 モックオブジェクト
  4.5.4 さらなるユニット間の対話の制御
 4.6 クラッシュの再現
 4.7 本章のまとめ
 4.8 ツール
 4.9 さらに詳しく学ぶ
 練習問題


まず大事なのが、問題の再現を、以下2つに分けて考えること。混合しないこと。

  • 環境の再現
  • 実行の再現


環境の再現は意外と面倒なのだけど、以下の観点で行えとのこと。
まーそれはそうですよね。OSのレベル変えたりとか、出来ればしたくないですしね。。
あと、一気に変えるより徐々に変えた方が、問題の原因を判明しやすいですね。

  • (以前の障害報告から推測した結果)最も問題の発生に関係すると考えられるもの、かつ
  • 簡単に変更できるもの(そして元に戻せるもの)


実行の再現に関しては、コントロール層を導入して、入力機能の呼び出しを補足しろって話。具体的に補足対象となるのは、以下。

  • データ
  • ユーザ入力
  • コミュニケーション
  • 時刻
  • 乱数
  • 動作環境
  • プロセススケジューリング

(意図しない影響をプログラムに与える可能性のあるもの)


この辺、いまいちこの本だとまとまってなくて分かりづらいんだけど、
例えばLinuxであればstraceを使ってシステムコールを監視しろとかみたいな話。

straceの出力使って、再生も行えるって書いてあるんだけど、
出力結果のシステムコールをCとかに書いて実行しろってことかな?多分。


ちなみにAIX等のUNIXだとtrussですね。
その辺は、以下がよくまとまってます。

http://yourpalm.jubenoum.com/archives/468


どのユニットで問題が発生してるかまで絞り込めているのであれば、
クラスの継承使ってコントロール層作れって話が4.5節。
メソッドをオーバーライドして、トレースログ吐き出すようにしろって話。
Cみたいなクラス機能がない言語だと、直接ソースコード書き換えるしかないですがね。。。

記録結果からモックオブジェクト作れば、再現テスト可能ですねって話。


あとはユーザーとのやり取りはあんましいいツールが無いとか。
一応紹介されてるのはHP社製のWinrunnerとか。
私は使ったことないんだけど、有名なんですかね?


最後にプログラムがクラッシュした時について。
この場合は、通常はクラッシュした時点で呼ばれてた関数がログとして残るのだけど、
クラッシュ前のステータスは記録しないので、工夫が必要って話。
使ったことないから分からないけど、JavaだとReCrashってツールを使うのかな?



この章はちょっといまいち。
部分的にツールの紹介とかもしてるんだけど、十分に踏み込めていない感じ。
体系的な説明にするか、具体的な説明にするか、どっちつかずになっちゃってる。
大学のテキストとして使うなら、こっから発展学習するにはいいのかな?


あんまし深くやってると時間ばかりかかるので、とりあえず先進みます〜。

第3章 プログラムを失敗させる

http://www.amazon.co.jp/dp/4873115930/


問題が発生したら、以下に挙げるように何度も同じテストの実施が必要です。
何度も実施するんだから、デバッグ用テスト自動化しておきましょうねって話。
(本書では、未知の問題の検出を目的としたものを妥当性確認テスト、
 既知の問題の発見を目的としたものをデバッグ用テストと呼んでいます。)

・問題を再現するために作成するテスト (4章)
・問題を単純化するために繰り返し行うテスト (5章)
・プログラムがうまく修正できたか確認するために再度行うテスト (15章15.4節)
・同様の問題 (または類似の問題)が今後発生しないよう、新しいバージョンのソフトウェアをリリースする前に再度行うテスト。
 このようなテストを回帰テスト(リグレッションテスト)とも言う (16章)


デバッグ用テストを、まず以下の3つのレイヤーに分けてそれぞれの長所・短所を記しています。

・プレゼンテーション層
 ➡ユーザーとのやり取りをシミュレートするテスト
・機能層
 ➡機能の直接テストをシミュレートするテスト
・ユニット層
 ➡単体テスト


一言で書くと、プレゼンテーション層でのテストは非推奨。
なぜならプレゼンテーション層は一番変わりやすい部分だから。
変更のたびにテスト資産の修正が必要になるのは賢くない。
ユニット層へのテストと機能層へのテストの自動化の組み合わせを推奨。
文中には明示してないけど、プレゼンテーション層のテストは手動でやれって話だと思う。
(少なくとも私の経験的には、プレゼンテーション層のテスト自動化はあんまし賢くない。)


さて、ここで大事なのが設計の善し悪しがデバッグのコストを大きく左右するってこと。
例えばプレゼンテーション層と機能層とが分離してないプログラムでは、
プレゼンテーション層でテストするしか無くなるため、
テストの自動化を諦めるか、変更覚悟で自動化テストを作るしか無くなってしまう。


だから設計の勉強もちゃんとしないと駄目だよって話。
一例として紹介しているのが、MVCと依存関係逆転の原則(DIPDependency Inversion Principle)。
この辺の詳細はネットでたくさん情報あるので、そちらで。



とりあえず3章はこんな感じ。
まだまだこの辺は序の口ってとこですね〜。
さてさて4章はどうなりますか。

第2章 問題の管理

http://www.amazon.co.jp/dp/4873115930/


発生した障害や、テスト中に見つかった問題は、ちゃんんと管理してトラッキングしましょうって話。



ポイントは「障害報告」の書き方。
問題を再現するために必要かつ十分な情報を書くこと。

情報が少ないと再現出来ないし、
かと言って情報過多だと、データベースが重複する情報で溢れてしまう。

これ、SEとしては当たり前の話なんだけど、意外と出来てない人が多い。
私は製品部門にいるので、いろんな障害報告を受ける側なんだけど、
全然情報足りないで解析依頼がきたり、逆に製品の問題かどうかも絞り込めずに丸投げされることも多い。
こういう技術力低い人ばっかだと、そりゃ残業も増えますわな。笑。



面白かったのは問題管理とテストケースの関係。
問題が見つかったってことは、テストケースが不十分だったってことだから、
テストケースを作成して、その時点で問題はクローズしろって話。
開発中に見つかった場合は、そもそも問題管理DBに登録せず、
最初からテストケースを書けと。
テストケースのステータスで問題が解決したかどうかチェック出来るし、
問題管理データベースが未解決の問題だらけだとエンジニアのモチベーションが下がると。



最後はツール使えって話。
問題管理、バージョン管理、変更管理を連携して、ちゃんとトラッキングすること。



2章はまだ触りって感じですね。でもそれなりに気付きもあってよかったです。
3章以降にも期待〜☆

Vmail

Vim Advent Calendar 2012 の 25 日目の記事です。
これ、参加=記事を書くってことだったんですね。
ちゃんとみてなかったので気づかず、今日連絡もらって気がつきました。。。

Vim暦は長いけど、Advent Calenderに書くほど使いこなしてない(- -;;)
でもさすがにドタキャンは出来ないので、Hacker Newsから最近の記事でポイント高いVim関連のものを翻訳しました。
全部訳してると明日になりそうなので、特に重要なポイントだけ翻訳しています。
これでお許しを。。。

Vmail



Vmailは、GmailVimから操作可能とするツールです。

画像1
画像2
画像3

CUI操作を好む多くのユーザーのために、Vmailは開発されています。


前提条件

  • Gmailアカウント
  • 新しいバージョンのVim (VmailはVim 7.3を対象として開発されています)
  • SSLサポートでコンパイルされたRuby 1.9.0以上 (VmailはRuby 1.9.2を使用して開発)
  • libsqlite3-devとsqlite3 (apt-get、brewyum、emerge等で導入)
  • text-only-mode web browser (w3m, elinks, or lynx) (HTMLメールを表示するために必要)

Ruby 1.9.2の導入には、RVM Version Managerの使用を推奨。

現在最新のVmailは、Unix環境を想定。

GmailアカウントはIMAP-enabledであること。

HTMLの表示にelinksを使いたいのであれば、こちらを参照。



インストール

gem install vmail

"vmail -h"とタイプして、Vmailのヘルプが表示されればインストール成功です。
もしPATHの問題でvmailコマンドを呼べなければ、システム環境を添えて報告していただけると幸いです。

sudo gem install vmail

sudoで実施することで、もしかしたらうまくいくかもしれません。また、rbenyを使っていると、rbeny rehashが問題を起こすことにご注意下さい。
# sudoからrbenyまでの訳は怪しいです。

gem install vmail

Vmailは鋭意開発中です。最新のVmailを入手するには、上記のように再度"gem install vmail"コマンドを実行するだけです。

gem uninstall vmail

上記コマンドで、簡単にVmailの削除が実行されます。
ただし、一部のファイルは削除されません。これについては後述します。


構成定義

Vmailの構成定義ファイルは、.vmailrcという名前で作成します。YAMLフォーマットとし、~/.vmail/defaults/に保存して下さい。
ホームディレクトリに作成しても構いません。この場合、Vmail実行時に構成定義ファイルは~/.vmail/defaultsに移動(move)されます。

以下に、.vmailrcのサンプルを示します。

username: dhchoi@gmail.com
password: password
name: Daniel Choi
signature: |
--
Sent from Vmail. http://danielchoi.com/software/vmail.html

.vmailrcへの定義で、通常使用しない文字(unusual characters?)を使用する場合は、クォーテーションで囲って下さい。

パスワードは記入しなくても構いません。この場合、Vmail起動ごとに手動でパスワードを入力して下さい。

メール送信時に、必ずccに入れたいアドレスがある時は、always_cc:をご使用下さい。同様に、必ずbccに入れたいアドレスがある時は、always_bcc:をご使用下さい。

shell scriptで署名を入れたい場合は、以下に示すようにsignature_script:をご使用下さい。

username: dhchoi@gmail.com
password: password
name: Daniel Choi
signature_script: /home/choi/bin/vmail_signature.sh

vim_opts:を使用すれば、Vmail起動時に任意のVimコマンドを実行できます。例えばカーソル行にフォーカスを当てたい時は、以下のように.vmailrcを作成します。

vim_opts: set cursorline

複数のGmailアカウントを使用したいときの構成定義の指定方法については、こちらを参照下さい。

FirewallによりIMAPがブロックされる時の対応方法については、こちらを参照下さい。

日付フォーマットを変更するには、date_format:を指定下さい。以下は、strptime互換(strptime-compatible)の日付フォーマット例です。フォーマットをクォーテーションで囲むことにご注意下さい。

date_format: '%b %d %I:%M%P'
date_format_previous_years: '%b %d %Y'


連絡先の入力補完機能

VmailはVimのオートコンプリーション機能を使って、メールアドレスの入力補完を行います。この機能を使用するために、連絡先一覧を補完するvmail-contacts.txtファイルを作成します。"-g"オプションを付けてVmailを実行すると、最新500通のメールの受信者とccより連絡先を集め、vmail-contacts.txtを作成します。"-g"オプションに数値を指定することで、対象とするメール数を指定出来ます。


Vmailの起動

構成定義ファイル(と必要であれば連絡先ファイル)を作成しましたら、Vmailを開始出来ます。
以下のコマンドでVmailを起動します。

vmail

Vmailが起動すると、Gmailの受信ボックスより最新100通が表示されます。

Vmail起動時にラベル名を指定すれば、指定したラベルのメールのみ表示されます。

vmail starred

以下のように、ラベル指定に加えて、検索パラメータの指定も可能です。

vmail important from barackobama@whitehouse.gov


メッセージの閲覧


Vmailを起動すると、受信メッセージの一覧が表示されます。カーソルを移動して、表示したいメールを指定する(ENTERを押す)ことで、メールの中身を確認出来ます。メールを指定すると、Vmailの画面は2分割され、メールの中身は下半分に表示されます。メールの指定にENTERキーを押すと、カーソルがメール本文に移動してしまいます。ENTERの代わりに"l"(Look)でメールを指定すれば、カーソルはメール一覧をさしたままとなります。

メール本文にカーソルがある状態で、スペースキーを押すと、メール本文が画面全体に表示されます。スペースキーの代わりに、"C-w"もしくは"C-o"でも構いません。全画面表示から抜けるには、スペースキーかENTERキーを押して下さい(ENTERキーを押した場合は、カーソルがメール一覧に移動します)。

リスト一覧にカーソルがある状態で、スペースキーを押すと、2分割画面から抜けてメール一覧のみの表示となります。スペースキーの代わりに、"C-w"もしくは"C-o"でも構いません。

2分割画面で、メール一覧とメール本文とでカーソルを移動するにはENTERキーを押して下さい。ENTERキーの代わりに、"C-w"もしくは"C-o"でも構いません。

メッセージ一覧ビューで、次のメッセージにカーソル移動するには""もしくは"j"を押して下さい。同様に、前のメッセージにカーソル移動するには""もしくは"k"を押して下さい。

Vmail起動時に全メールを受信しきらなかった時は、以下のようなメッセージが、メッセージ一覧の最下部に表示されます。

> Load 100 more messages. 156 remaining.

読み込めなかったメールのロードには、上記メッセージにカーソルを合わせてENTERキーを押して下さい。

参考情報:"G"と打てば、メッセージ一覧の最下部にジャンプします。

未読メッセージには"+"マークがつきます。

RFC822バージョンのメッセージを閲覧するには、"R"と打ってメールを閲覧下さい。


メールの送信

"c."と打つと、新規メールの作成画面にうつります。

受信メールへの返信は"r"と打って下さい。

全員に返信する場合は、"a"と打って下さい。

メールの転送には、"f"と打って下さい。

上記コマンドでメール作成画面を開くと、以下のようなメールヘッダが表示されます。

from: Daniel Choi
to:
subject:

from:欄は、作成した.vmailrcからアドレスが自動的に記入されます。必要であれば、cc:欄とbcc:欄を追加可能です。

前述のvmail-contacts.txtファイルを作成していれば、送信先アドレスの入力補完機能が使えます。名前かメールアドレスを途中まで記入し、"C-x"もしくは"C-u"をタイプすることで、入力補完が実行されます。"C-n"か"C-p"か"C-u"と打ち、意図するメールアドレスを選択してください。さらに送信先アドレスを記入するには、スペースキーを打つか、何か文字を打って下さい。

Make sure your email addresses are separated by commas and that they all ultimately appear on the same, unbroken line for each field. Rejoin the lines if breaks get inserted.
# すみません、この一文の意味がわかりませんでした。
# 自分のメールアドレスが途中で意図せず分割されていたりしないよう注意ってことでしょうか??

ヘッダを書いたら、メール本文を書いて下さい。ヘッダとメール本文の間に空白行を挟むことを忘れないようご注意下さい。

When you're done writing, send the message by typing ,vs in normal mode.
メールを書き終わったら、ノーマルモードで",vs"と打ち、送信して下さい。

メール作成中に、":w"と打てば下書き保存されます。

:w my_draft_filename.txt

メールを下書き保存する場合は、*.txtとして下さい。それ以外のフォーマットはメールと認識されません。

":wq"と打つと、メールを下書き保存してVmailを抜けます。メールを下書き保存した後、ノーマルモードで",q"と打つと、メール一覧画面に戻ります。

下書き保存したメールを再度編集する時は":e my_draft_filename.txt"と打って下さい。既にメール作成画面にいる時は、":e!"と打って下さい。また、2分割画面の状態で下書き保存したメールを編集したい時は":sp"と打って下さい。

どの状態でも、ノーマルモードで",q"と打てばメール作成画面から抜けられます。

下書きメッセージを直接送信するには、vmailsendコマンドw実行して下さい。

vmailsend < my_message.txt

vmailsendコマンドの使用には、正しい.vmailrcの設定がされており、かつ.vmailrcにパスワードが保存されている必要があります。


Vmailの終了

どの画面でも構わないので、",qq"と打てばVmailは終了します。":qall!"も同様です。


ヘルプ

",?"と打つと、ヘルプ画面がブラウザに開きます。



まったく予期してなかったので、仕事の合間に書いてます。
検証していないところが多いので、確認して随時更新していく予定です。

問題3.3

http://www.amazon.co.jp/dp/4839942390


この辺も簡単ですね。さくさくっと進みます。

問題

皿が積み上がっている状況をイメージしてください。もし、高く積み上がり過ぎたら倒れてしまうでしょう。ですから、実生活ではスタックがある領域を越えたとき、新しいスタックを用意することになるでしょう。これをまねたデータ構造SetOfStacksを実装してください。SetOfStacksはいくつかのスタックを餅、スタックのデータが一杯になったらスタックを新たに作らなければなりません。また、SetOfStacks.push()とSetOfStacks.pop()は普通のスタックのようにふるまうようにしてください(つまり、pop()は通常の1つのスタックの場合と同じ値を返さなければなりません)。

importしてるpystack.pyは問題3.2で作ったものです。


http://d.hatena.ne.jp/takuto1981/20121222/1356190983


pystack_max10.py

#encoding: utf-8
from pystack import PyStack
STACK_SIZE = 10


class PyStackMax10(PyStack):
    '''
    最大10個までしか入れられないスタッククラス
    >>> stack_instance = PyStackMax10()
    >>> stack_instance.push(0)
    >>> stack_instance.push(1)
    >>> stack_instance.push(2)
    >>> stack_instance.push(3)
    >>> stack_instance.push(4)
    >>> stack_instance.push(5)
    >>> stack_instance.push(6)
    >>> stack_instance.push(7)
    >>> stack_instance.push(8)
    >>> stack_instance.push(9)
    >>> stack_instance.push(10)
    False
    >>> stack_instance.pop()
    9
    >>> stack_instance.push(10)
    >>> stack_instance.size()
    10
    '''
    def push(self, num):
        if len(self.stack_data) == STACK_SIZE:
            return False
        else:
            self.stack_data.append(num)

    def size(self):
        return len(self.stack_data)


def _test():
    import doctest
    doctest.testmod()


if __name__ == '__main__':
    _test()

3-3.py

#encoding: utf-8
from pystack_max10 import PyStackMax10
STACK_SIZE = 10


class SetOfStacks():
    '''
    1つのスタックサイズが10個のスタックを複数もつクラス。
    >>> stacks_instance = SetOfStacks()
    >>> stacks_instance.push(0)
    >>> stacks_instance.push(1)
    >>> stacks_instance.push(2)
    >>> stacks_instance.push(3)
    >>> stacks_instance.push(4)
    >>> stacks_instance.push(5)
    >>> stacks_instance.push(6)
    >>> stacks_instance.push(7)
    >>> stacks_instance.push(8)
    >>> stacks_instance.push(9)
    >>> stacks_instance.push(10)
    >>> stacks_instance.push(11)
    >>> stacks_instance.push(12)
    >>> stacks_instance.pop()
    12
    >>> stacks_instance.pop()
    11
    >>> stacks_instance.pop()
    10
    >>> stacks_instance.pop()
    9
    >>> stacks_instance.pop()
    8
    >>> stacks_instance.pop()
    7
    >>> stacks_instance.pop()
    6
    >>> stacks_instance.pop()
    5
    >>> stacks_instance.pop()
    4
    >>> stacks_instance.pop()
    3
    >>> stacks_instance.pop()
    2
    >>> stacks_instance.pop()
    1
    >>> stacks_instance.pop()
    0
    '''
    def __init__(self):
        self.set_of_stacks = []

    def push(self, num):
        if len(self.set_of_stacks) == 0 \
                or self.set_of_stacks[-1].size() == STACK_SIZE:
            new_stack = PyStackMax10()
            new_stack.push(num)
            self.set_of_stacks.append(new_stack)
        else:
            self.set_of_stacks[-1].push(num)

    def pop(self):
        if len(self.set_of_stacks) == 0:
            return None
        else:
            return_data = self.set_of_stacks[-1].pop()
            # 1つのスタックが空になったら、スタックセットから除去する。
            if self.set_of_stacks[-1].size() == 0:
                self.set_of_stacks.pop()
            return return_data


def _test():
    import doctest
    doctest.testmod()


if __name__ == '__main__':
    _test()