カウンタ付きクロージャ

LL Golf のラストでshinh先生がおっしゃっていたことを受けて呼ばれた回数を内外から参照できるクロージャというのを実現できないかと試行錯誤した結果↓

ExpandoMetaClass.enableGlobally()
({
  def loopCounts = [:]
  def getProp = { String name ->
    def c = delegate.class
    if(name == '_') return loopCounts[c]
    def m = c.metaClass.methods.find{ it.name =~ "get(?i)$name" }
    if(!m) throw new MissingPropertyException(name, c)
    m.invoke(delegate)
  }
  Closure.metaClass.invokeMethod = { String name, args ->
    def c = delegate.class
    if(name == 'doCall'){
      args = args ?: (Object[]) [null] // prevents redundant doCall
      loopCounts[c] = loopCounts[c]?.next() ?: 0
      delegate.resolveStrategy = Closure.TO_SELF
      c.metaClass.getProperty = getProp
    }
    c.metaClass.getMetaMethod(name, args).invoke(delegate, args)
  }
})()

cl = { _ }
assert [cl(), cl(), cl(), cl._] == [0, 1, 2, 2]
assert 'hoge'.collect{ [(it): (0.._).collect{ _ }] } ==
  [["h":[0]], ["o":[1, 2]], ["g":[3, 4, 5]], ["e":[6, 7, 8, 9]]]

一応動くようだがマズイ副作用があるかも。getProp の後半辺りが特に怪しい。

+

同様のことが可能な他言語って何があるだろうか。Smalltalk, Io, ...Ruby?*1

*1:Rubiniusの力でも借りないと無理?