循環バッファ、サイクリックバッファ、リングバッファは、単一の固定サイズのバッファを、端と端がつながっているかのように使うデータ構造です。
循環バッファは最初は空で、あらかじめ決められた長さを持ちます。 たとえば、これは7要素のバッファです:
[ ][ ][ ][ ][ ][ ][ ]
バッファの真ん中に1が書き込まれたとします(循環バッファでは、正確な開始位置は問題になりません):
[ ][ ][ ][1][ ][ ][ ]
次に、さらに2つの要素、2と3が追加され、1の後に続けて入るとします:
[ ][ ][ ][1][2][3][ ]
そのあとでバッファから2つの要素を取り除くと、バッファの中で最も古い値が取り除かれます。 この場合に取り除かれる2つの要素は1と2で、バッファには3だけが残ります:
[ ][ ][ ][ ][ ][3][ ]
バッファに7つの要素が入ると、完全にいっぱいになります:
[5][6][7][8][9][3][4]
バッファがいっぱいになるとエラーが発生し、スロットが空くまでこれ以上書き込めないことをクライアントに知らせます。
バッファがいっぱいのとき、クライアントは強制書き込みで最も古いデータを上書きすることを選べます。 この場合、さらに2つの要素AとBが追加され、3と4を上書きします:
[5][6][7][8][9][A][B]
3と4はAとBに置き換えられ、バッファの中で最も古いデータは5になりました。 最後に、2つの要素を取り除くと、返されるのは5と6で、バッファはこうなります:
[ ][ ][7][8][9][A][B]
空きがあるので、ここでクライアントが再び上書きを使ってCとDを保存すると、使われるのは前に5と6が入っていた場所で、7と8の場所ではありません。 7は依然として最も古い要素で、バッファは再びいっぱいになります。
[C][D][7][8][9][A][B]
コードの中で例外をカスタマイズし、さらにraiseする必要があることもあります。そのようなときは、エラーの原因が何であるかを示す意味のあるエラーメッセージを必ず含めるようにしましょう。これによりコードが読みやすくなり、デバッグも格段にはかどります。
カスタム例外は、新しい例外クラスを作ることで定義できます(詳しくはclassesを参照してください)。こうしたクラスは通常、Exceptionのサブクラスです。
エラーの原因が、ある例外型から派生したものだとわかっている場合もあります。そのようなときは、_Exception_クラスの下にあるbuilt in error typesのいずれかを継承するという方法があります。エラーを送出するときも、意味のあるメッセージを含めるようにしましょう。
この演習では、2つの_カスタム例外_を作成する必要があります。1つは循環バッファが満杯のときに送出する、つまり「スロー」する例外で、もう1つは空のときの例外です。テストに合格するのは、適切な例外をカスタマイズし、その例外をraiseし、適切なエラーメッセージを含めた場合だけです。
built-in exceptionをカスタマイズするには、その例外を継承したclassを作成します。メッセージ付きでカスタム例外を送出するときは、そのメッセージをexception型の引数として書きます。
# subclassing the built-in BufferError to create BufferFullException
class BufferFullException(BufferError):
"""Exception raised when CircularBuffer is full.
message: explanation of the error.
"""
def __init__(self, message):
self.message = message
# raising a BufferFullException
raise BufferFullException("Circular buffer is full")