スタッククラス(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 の中に置くのも、同じ理由です。
やってみよう
stackOps(ops) を書いてください。中に自分でクラスを 1 つ定義し、値を積むメソッドと、一番上を取り除くメソッドを持たせます。ops は ["push", 3] や ["pop"] が並んだ配列なので、先頭の文字列を見て呼び分けます。取り除いた値は使わないので捨てて構いません。最後に、内部で持っているリストを配列として返します。空の状態で取り除く操作は来ません。
要件
- 関数
stackOps(ops)を実装し、操作後のスタック内容を配列で返す - 内部に
Stackクラスを定義し、push/popメソッドを持たせる - ops の各要素は
["push", value]または["pop"]の形に対応する
入出力例
stackOps([["push",1],["push",2],["pop"],["push",3]]) → [1,3]
stackOps([["push",5],["push",10],["push",15],["push",20]]) → [5,10,15,20]
stackOps([["push",1],["push",2],["pop"],["pop"],["push",99]]) → [99]
stackOps([["push",1],["pop"],["push",2],["pop"],["push",3]]) → [3]
stackOps([["push",42]]) → [42]