uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

return省略のルール

Groovyではreturn文において、returnキーワードの省略ができます。たとえば

boolean even(n) {
   if (n % 2 == 0) {
     return true
   }
   else {
     return false;
   }
}

はこうかけます。

boolean even(n) {
   if (n % 2 == 0) {
     true
   }
   else {
     false;
   }
}

でも、こうは書けません。

int sum(n) {
  int sum = 0;
  while (n-- > 0) {
    sum += n;
  }
  // 「return sum」を省略することはできない。
}

whileの最後の回のループでの評価値がメソッドの戻り値としてreturnされるということはないです。「最後に評価した値がreturn値として用いられる」というルールではないからですね。

Groovyのreturnキーワード省略のルールは、こういうものです。

直後にメソッドから脱出することがわかっている場所での式文は、「return」キーワードを省略したreturn文だとみなす。

これは純粋に構文上のルールです。実際、上の1番目と2番目の例では生成されるバイトコードは同一です。

それに対して、3番目のコードは、

int sum(n) {
  int sum = 0;
  while (n-- > 0) {
    return sum += n;
  }
}

の省略形と解釈されると困りますし、そう解釈されることはありません。

とはいうものの、eachなどのクロージャを引数に取る繰り返しメソッドは、返り値を持っていますので、

int sum(n) {
  int sum = 0;
  (1..n).each {
    sum += n
  }
}

が期待するように動く可能性はあるわけです。つまり上では、

int sum(n) {
  int sum = 0;
  return (1..n).each {
    sum += n
  }
}

のようにreturnが省略されているとみなされるので、eachメソッドの呼び出し結果を返すからです。でも残念ながら、現在のeachの実装は、評価したクロージャの値を返すようにはなっていないので、駄目です。以下のように自前で作ればOK。

def threeTimes(Closure clos){
  clos.call()
  clos.call()
  return clos.call()  // 3回実行し、最後の回の評価値を返す
}

def foo(n) {
  def sum = 0
  threeTimes{ sum += n } // return不要
}

assert foo(3) == 9