Squeak Smalltalk で無名関数を再帰する


404 Blog Not Found:perl - で(Recall()|arguments.callee()|&?BLOCK()) と似たような方法で。

[:nn | nn < 2 ifTrue: [nn] ifFalse: [
	nn * ((Compiler evaluate: thisContext decompileString) value: nn - 1)]
] value: 10   "=> 3628800 "


古いタイプの Smalltalk である Squeak Smalltalk のブロック(ブロックコンテキスト)は再入できないので、せっかく thisContext で無名関数(=ブロック)自身を得ることができても、それを再帰的に評価することができません。いったんコード文字列に戻して再コンパイルしてそうして得られた新しいブロックに改めて value: nn - 1 を送っています。



なお、新しいタイプ〜というか、おそらく Squeak Smalltalk 以外〜の Smalltalk である Cincom Smalltalk(VisualWork)のブロック(ブロッククロージャ)は再入できるので、比較的すなおに書けるようです。ただ、thisContext は相変わらずブロックコンテキストを返すので、receiver を送って無名関数自身であるブロッククロージャを得る必要があります。

[:nn | nn < 2 ifTrue: [nn] ifFalse: [
    nn * (thisContext receiver value: nn - 1)]
] value: 10   "=> 3628800 "

ちなみに、Smalltalk とよく似た仕組みを持つ Rubinius Ruby でもほぼおなじ方針で同様の再帰が可能なようです。

proc{ |n| n < 2 ? n : n * MethodContext.current.env.call(n - 1) }.call(10)
=> 3628800