“アッカーマンの呪い”を Squeak Smalltalk で


まず素直に #ifTrue:ifFalse: を使った条件分岐で。関数なのでクラスは定義せずブロック(無名関数)を使います。

| ack |
ack := nil.
ack := [:m :n |
   m = 0 ifTrue: [
      n + 1
   ] ifFalse: [
      n = 0 ifTrue: [
         ack value: m - 1 value: 1
      ] ifFalse: [
         ack value: m - 1 value: (ack value: m value: n - 1)
      ]
   ]
].

ack value: 3 value: 4.  "=> 125 "


if のネストがちょっとイヤ〜ンな感じなので #caseOf:otherwise: を使ってすっきりさせます。true にメッセージを送るのはバッドノウハウです。^^;

| ack |
ack := [:m :n |
   true caseOf: {
      [m = 0] -> [n + 1].
      [n = 0] -> [ack value: m - 1 value: 1].
   } otherwise: [ack value: m - 1 value: (ack value: m value: n - 1)]
].

ack value: 3 value: 4.  "=> 125 "


このままだと ack(4,1) もままならないので、メモ化をします。

| ack memo |
memo := Dictionary new.
ack := nil.
ack := [:m :n |
   memo at: {m. n} ifAbsentPut: [
      true caseOf: {
         [m = 0] -> [n + 1].
         [n = 0] -> [ack value: m - 1 value: 1].
      } otherwise: [ack value: m - 1 value: (ack value: m value: n - 1)]
   ]
].

ack value: 3 value: 4.  "=> 125 "
ack value: 4 value: 1.  "=> 65533 "

それでも ack(4.2) だとしばらくはがんばりますが結局落ちてしまうようです(2 GHz Core i7, OS X 向け CogVM 使用)。残念。