クックの分類を意識した、Python による「未定義な遷移の解釈」の実装


Diary?::2007-10-12 - (22:35) 経由で、未定義な遷移の解釈 -- 3つの立場 - 檜山正幸のキマイラ飼育記 のうち未定義がエラーを(値として…)返す場合を同じく Python で。

関数とメソッド(クラス内定義関数)の区別を持つ Python は、クックの ADT と PDA(OOP)の両方のスタイルをサポートできます(もちろん、情報隠蔽が十分でないので、いずれもかなり限定的にではありますが…)。両者の違いを際だたせるのによさそうな例に思えたので、少し遊んでみました。

クックの ADT を意識した場合

class err:
  def __str__(self):
    return "error"

class n(int):
  pass

def up(v):
  if isinstance(v,n):
    return n(v+1)
  elif isinstance(v,err):
    return v

def down(v):
  if isinstance(v,n):
    if v>0:
      return n(v-1)
    else:
      return err()
  elif isinstance(v,err):
    return v

def reset(v):
  return n(0)

proc = lambda v,f: f(v)
print reduce(proc,[n(1),up,reset])  #=> 0
print reduce(proc,[n(1),up,up,down])  #=> 2
print reduce(proc,[n(1),up,up,down,down,down,down])  #=> error

クックの PDA(OOP) を意識した場合

class Err:
  def __str__(self):
    return "error"
  def up(self):
    return self
  def down(self):
    return self
  def reset(self):
    return N(0)

class N(int):
  def up(self):
    return N(self+1)
  def down(self):
    if self>0:
      return N(self-1)
    else:
      return Err()
  def reset(self):
    return N(0)

proc = lambda v,f: getattr(v,f)()
print reduce(proc,[N(1),"up","reset"])  #=> 0
print reduce(proc,[N(1),"up","up","down"])  #=> 2
print reduce(proc,[N(1),"up","up","down","down","down","down"])  #=> error


書いていて気が付いたのですが、クックはリスコフの ADT を、原義より狭い範囲に追いやってしまって、そうして空けたスペースに彼の OOPPDA)を据えようとしたのかもしれませんね。(というのもリスコフは、後者のスタイルについても、パフォーマンス面での不利を理由に CLU での採用は見送ったものの、ADT 実装スタイルの選択肢としては完全には排除しておらず、この点で両者とも ADT の範疇であるとも解釈できるので…)


追記
N.down() でやっている、0 以上であるという事実上の“型チェック”を N.__new__() に追い出してしまって、こんなふうに書くこともできますね。

class N(int):
  def __new__(cls,v):
    if v>=0:
      return int.__new__(cls,v)
    else:
      return Err()
  def up(self):
    return N(self+1)
  def down(self):
    return N(self-1)
  def reset(self):
    return N(0)