GroovyとJavaScriptのクロージャ

よく見るカウンタのクロージャをGroovyとJavaScriptで書いてみる。

Groovy版

def clo() {
  def i=0
  return {
    i++
  }
}

def a = clo()
println "a():${a()}" // a():0
println "a():${a()}" // a():1
println "a():${a()}" // a():2

def b = clo()
println "b():${b()}" // b():0
println "b():${b()}" // b():1
println "b():${b()}" // b():2

println "a():${a()}" // a():3
println "b():${b()}" // b():3

JavaScript

function clo() {
  var i=0;
  return function(){
    return i++;
  };
}

var a = clo();
console.log("a():" + a()); // a():0
console.log("a():" + a()); // a():1
console.log("a():" + a()); // a():2

var b = clo();
console.log("b():" + b()); // b():0
console.log("b():" + b()); // b():1
console.log("b():" + b()); // b():2

console.log("a():" + a()); // a():3
console.log("b():" + b()); // b():3


メソッド(関数)を定義しない(という表現で良いのだろうか?)場合は以下のようになる。
Groovy版

def clo = {
  def i = 0
  return {
    i++
  }
}

def a = clo()
println "a():${a()}" // a():0
println "a():${a()}" // a():1
println "a():${a()}" // a():2

def b = clo()
println "b():${b()}" // b():0
println "b():${b()}" // b():1
println "b():${b()}" // b():2

println "a():${a()}" // a():3
println "b():${b()}" // b():3

JavaScript

var clo = function(){
  var i=0;
  return function(){
    return i++;
  };
}

var a = clo();
console.log("a():" + a()); // a():0
console.log("a():" + a()); // a():1
console.log("a():" + a()); // a():2

var b = clo();
console.log("b():" + b()); // b():0
console.log("b():" + b()); // b():1
console.log("b():" + b()); // b():2

console.log("a():" + a()); // a():3
console.log("b():" + b()); // b():3

[Groovy]追記(2012/01/11)

よくわかってないけどこうかくとエラーになる。

def clo() {
  x ->
  return {
    x++
  }
}

「->」の前に書かれたものはパラメータになるらしい。

def clo = {
  x ->
  return {
    x++
  }
}

def a = clo(1)
println "a():${a()}" // a():1
println "a():${a()}" // a():2
println "a():${a()}" // a():3

パラメータなしで呼び出すとエラー

def clo = {
  x ->
  return {
    x++
  }
}

def a = clo()
println "a():${a()}" // java.lang.NullPointerException

初期化すれば大丈夫

def clo = {
  x=0 ->
  return {
    x++
  }
}

def a = clo()
println "a():${a()}" // a():0
println "a():${a()}" // a():1
println "a():${a()}" // a():2

def b = clo(9)
println "b():${b()}" // b():9
println "b():${b()}" // b():10
println "b():${b()}" // b():11

また、引数が一個以下なら「->」を省略可能
とあるが、

def clo = {
  return {
    x++
  }
}

def a = clo()
println "a():${a()}" // groovy.lang.MissingPropertyException

となるのはわかる。xが宣言されていないから。

ただ暗黙変数itで書いたこれが何でエラーになるのかが全然わからなかった。

def clo = {
  return {
    it++
  }
}

def a = clo()
println "a():${a()}" // java.lang.NullPointerException

//def a = clo(1)
//println "a():${a()}" // java.lang.NullPointerException

ちなみにこれもエラーだった。

def clo = {
  it=0 ->
  return {
    it++
  }
}

def a = clo()
println "a():${a()}" // java.lang.NullPointerException

//def a = clo(1)
//println "a():${a()}" // java.lang.NullPointerException

結果、いろいろ試したら何となくわかった。

def clo = {
  return {
    it
  }
}

def a = clo()
println "a():${a()}" // null
def b = clo(4)
println "b():${b()}" // null

retrunで返却されるクロージャの中のitはa(とかb)の実行時の引数を見ていたらしい。

def clo = {
  return {
    it
  }
}

def a = clo()
println "a(1):${a(1)}" // 1
def b = clo(4)
println "b(7):${b(7)}" // 7


参考になりました。
http://groovy.codehaus.org/Japanese+Closures
groovyクロージャについて徒然と - uehaj's blog