なごり雪:コードを巡る旅の縁(よすが)に
同じ景色を眺めるにしても、思い入れがあるのとないのとでは、その感動も違ってきます。そこで、漠然とコードを眺めるのではなく、構造化ブログラミング〔SP〕とオブジェクト指向プログラミング〔OOP〕との違いを示しながら、その相互理解を深めます。
そこに生命体が誕生するのは
任意の生命体は、隣接する他の生命体の数によって次世代の状態(誕生/生存/死滅)が決定します。
class LifeGamePanel(JPanel):
def locateLife(self):
points = [(x, y)
for y in range(self.dim)
for x in range(self.dim)]
for x, y in points:
self.items.append(Life(x, y))
def addNeighbor(self):
for e in self.items:
self.addNeighbor1(e)
def addNeighbor1(self, life):
ix = life.x
iy = life.y
points = [(x, y)
for y in (iy-1, iy, iy+1)
for x in (ix-1, ix, ix+1)]
for x, y in points:
if (x, y) == (ix, iy): continue
life.add(self.detect(x, y))
def detectLife(self, e):
edge = Life.Edge
return self.detect(e.x/edge, e.y/edge)
メソッドlocateLifeは、生命体Lifeをパネル全面に配置します。
メソッドaddNeighborは、各生命体ごとに隣接する8つの生命体(Nullを含む)を登録します。実際の処理は、補助関数addNeighbor1に委ねます。
メソッドdetectLifeは、イベントeが発生したときに、その座標e.x/e.yに位置する生命体を検出detectします。
class LifeGamePanel(JPanel):
def nextGeneration(self):
self.updateFuture()
self.updatePresent()
self.repaint()
def updateFuture(self):
for e in self.items:
e.updateFuture()
def updatePresent(self):
for e in self.items:
e.updatePresent()
メソッドnextGenerationは、生命体の状態(誕生/生存/死滅)を更新します。メソッドupdateFuture/updatePresentは、現世/次世代の生命体の状態を更新します。どちらも、実際の処理は各生命体eに委ねます。
ゲームの達人:ゲームを始める前に
パズルゲームでは、空いたマス目とそれを囲む4つのコマとの対話が必要でした。ライフゲームでは、対象の生命体とそれを囲む8つの生命体との対話が必要です。
パズルゲームではコマの配置が変るので、移動する度にそれを囲むコマを特定する必要がありました。ライフゲームでは生命体の配置は変わりませんが、それを囲む8つの生命体との対話が鍵となります。なぜなら、オブジェクト指向の醍醐味は、そのコラボレーションにあるからです。
class LifeGamePanel(JPanel):
def initialize(self):
self.items = []
self.locateLife()
self.addNeighbor()
メソッドinitializeは、生命体をパネルに配置します。このとき、ボードからはみ出さないように注意します。最後に、隣接する生命体を登録します。
ゲームの達人:if/switch が消え、最後に生き残るのは
巨大な一枚岩のクラスは、孤独な生命体のようなものです。アジャイル開発では、そのようなクラスは短命に終わります。
class Life:
def updateFuture(self):
n = self.neighbors()
if n == 2:
self.future = self.present
elif n == 3:
self.future = True
else:
self.future = False
メソッドupdateFutureは次世代の生命体の生死を決定します。neighborsを利用して、その周囲に生存する生命体の数を獲得します。要求仕様(ルール)に従って、その周囲に2つの生命体が生存する場合、現在の生死状態をそのまま次世代に引き継ぐのでpresentを設定します。その周囲に3つの生命体が生存する場合、次の世代でもその生命体は生存するのでTrueを設定します。上記以外の場合、次の世代でその生命体は死滅するので、Falseを設定します。
class Life:
def neighbors(self):
return len([None for e in self.neighbor if e.present])
メソッド neighbors は、隣接する生命体の生存数をリターン値とします。
switchの魅力は忘れ難く「なごり惜しい」気持ちは、分からないでもありません。Pythonに限らず、ハイブリッド型の言語では、純粋なOOP言語と違ってプログラマーの選択に委ねられます。switch文は、ハードコーディングの象徴であり、OOPではタブーのひとつとされています。しかし、その誘惑に抗えないプログラマー心理もあってか、old-timerから寄せられるswitch待望論があるのも事実です。3項演算子に相当する構文が導入されるなど、近い将来switchが導入されるなら、old-timerには朗報です。このような論議に興味のある皆さんは、1970-80年代の論文を紐解くのも一興です。