スタッククラス(push と pop)
前回は、値と操作を 1 枚の設計図にまとめました。今回は、その設計図に「途中の状態」を持たせます。
何が積まれているかを、変数名では追えない
ブラウザの「戻る」を思い浮かべてください。開いたページが 1 枚ずつ上に積まれ、戻るを押すと一番上の 1 枚だけが外れます。最後に置いたものが最初に出るこの出入りの仕方を LIFO と呼びます。
積まれている枚数は、操作してみるまで分かりません。page1 page2 page3 と変数を並べる書き方は、3 枚を超えた時点で行き詰まります。要るのは「1 本のリストをずっと持ち続けてくれる入れ物」です。
リストごとインスタンスに持たせる
属性は __init__ の中で作ります。
Python
class Notepad:
def __init__(self):
self.lines = []
def write(self, text):
self.lines.append(text)
def count(self):
return len(self.lines)Notepad() を呼ぶたびに self.lines = [] が走るので、新しい空のリストが 1 本ずつ用意されます。write を何回呼んでも、その回数ぶんの状態は self.lines に残り続けます。呼ぶ側はリストの存在を知らないまま、write と count だけを使えます。
関数だけで書いていたときは、途中経過を引数で渡して戻り値で受け取り、次の呼び出しへまた渡す、を繰り返す必要がありました。状態をインスタンスに持たせると、この持ち回しがまるごと消えます。渡し忘れも起きません。
空のリストを書く場所を 1 段間違える
Python
class Notepad:
lines = [] # ここに書くと全インスタンスで 1 本を共有する
def write(self, text):
self.lines.append(text)こう書いてもエラーは出ません。しかし Notepad() を 2 つ作ると、片方に書いた行がもう片方からも見えます。リストが設計図そのものに 1 本だけ作られ、実物ごとには作られないからです。JavaScript で this.items = [] をクラス本体ではなく constructor の中に置くのも、同じ理由です。
やってみよう
ひな形の TODO を完成させてください。push と、空を確認する pop の本体を自分で書きます。入力を受け取る枠と説明つきの準備部分はそのまま使えます。
演習で使う書き方
本文の Notepad は属性の配列に文章を残します。演習では同じように値を保持し、後から入れた値を先に取り出します。例えば [8, 5] に pop() を使うと 5 が返り、[8] が残ります。空の配列では取り出せないので、空確認も含めて両操作を自分で書きます。入力操作を振り分ける部分は用意しました。
要件
- push と、空を確認する pop の本体を自分で実装する
- 関数名と引数、入力と戻り値の形を保つ。説明つきの準備部分を使い、TODO の仮の処理を置き換える