再帰呼び出しを再帰呼び出しなしで実現 を Squeak Smalltalk で

読者のみなさんにもgotoのある言語で試すことをオススメします。

再帰呼び出しを再帰呼び出しなしで実現 - 西尾泰和のはてなダイアリー

ということだったので goto も使える Squeak Smalltalk で書いてみました(違うw。

| VERBOSE stack functionResult showAny showMany total3 |
VERBOSE := true.
stack := OrderedCollection new.
functionResult := 0.

showMany := nil. "for compiler"

showAny := [:x |
   (x isKindOf: Integer) ifTrue: [
      Transcript show: x
   ] ifFalse: [
      showMany value: x
   ]
].

showMany := [:xs |
   Transcript show: '['.
   (1 to: xs size) do: [:i |
      showAny value: (xs at: i).
   ] separatedBy: [Transcript show: ', '].
   Transcript show: ']'
].

total3 := [:xs |
   | ENTRYPOINT result i LOOP x RETURNPOINT frame |
ENTRYPOINT := thisContext previousPc.
   result := 0.

   i := 0.
LOOP := thisContext previousPc.
   i := i + 1.
   x := xs at: i.

   VERBOSE ifTrue: [
      Transcript show: 'xs:'.
      showMany value: xs.
      Transcript show: ', x:'.
      showAny value: x.
      Transcript show: ', i:'; show: i; show: ', result:'; show: result; cr; endEntry.
   ].

   (x isKindOf: Integer) ifTrue: [
      result := result + x.
   ] ifFalse: [
      stack addFirst: {xs. i. x. result}.
      xs := x.
      RETURNPOINT := thisContext previousPc + 10.
      thisContext pc: ENTRYPOINT.

RETURNPOINT.
      frame := stack removeFirst.
      xs := frame first.
      i := frame second.
      x := frame third.
      result := frame last.

      result := result + functionResult
   ].
   i < xs size ifTrue: [thisContext pc: LOOP].

   stack isEmpty ifFalse: [
      functionResult := result.
      thisContext pc: RETURNPOINT.
   ].

   result
].

VERBOSE ifTrue: [World findATranscript: nil].
total3 value: #(1 (2 3) 4)  "=> 10 "
xs:[1, [2, 3], 4], x:1, i:1, result:0
xs:[1, [2, 3], 4], x:[2, 3], i:2, result:1
xs:[2, 3], x:2, i:1, result:0
xs:[2, 3], x:3, i:2, result:2
xs:[1, [2, 3], 4], x:4, i:3, result:6


goto の動作は簡単のため、戻ってきたい場所のプログラムカウンター(pc)をラベルに模した変数に保持し(LABEL := thisContext previousPc)、必要なときにその pc に飛ぶ(thisContext pc: LABEL)ことで実現しています。

しかしこの方法だと RETURNPOINT の pc は未通過のため保持できないので RETURNPOINT への代入については ENTRYPOINT に戻る前にオフセット値をハードコードして保持しなければならなかった点がちょっと心残りですね。^^;