uehaj's blog

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

getAtとマルチ代入の不思議な関係

groovyには、

def (a,b) = [1,2]
assert a == 1
assert b == 2

というようなマルチ代入*1の構文があります。
私は、マルチ代入分の右辺はリストである必要があるのかと思っていたのですが、こちらの記事を読むとそうではなくて、getAtが定義されていればよいそうです。変数の個数だけgetAtが呼ばれるという事になります。ということは

def (a,b) = [1:"x", 0:"y"]
assert a=="y"
assert b=="x"

こんなのもありですね。まてよ、

def (a,b) = "xy"
assert a=="x"
assert b=="y"

こ、これはいかがなものか…。

ちなみにもちろん

def (a,b) = ["xy", "vw"]
assert a=="xy"
assert b=="vw" 

です。

え、きもさが足りないって? では

def (int a, String b) = [3, "def"]
assert a == 3
assert b == "def"

こんなんはいかがでしょうか。

ちなみに、Groovyではもともと、メソッド呼び出しの引数に関して、

def foo(a,b){
  println "a=$a, b=$b"
}
foo([1,2])

のように、「カンマで区切られた複数のメソッドの仮引数に対して、実引数としてリストが与えられたら、リストが個々の引数に配分される」という仕様があります(list spread operator ’*'を使わなくても)。マルチ代入はこの仕様の一般化と考える事ができるかもしれません。

ハッ!ということは?!…怖い考えを思いついてしまいましたがまさか…

def foo(a,b){
  println "a=$a, b=$b"
}
foo("XY")  // ==>groovy.lang.MissingMethodException
foo([0:3, 1:4]) '// ==> groovy.lang.MissingMethodException

をやってみたら、これは駄目でした。メソッド引数の場合はgetAtからまないのね。ほっ。

ちなみに"XY"がa,bに分解されたら引数のパターンマッチングみたいなもんですが、Groovy 2.0の機能にリストアップされているので、将来的にはその種の事もできるようになるかもしれません。

*1:私はこれを「多重代入」と呼びたくない。なぜかというとC言語におけるa=b=cの「多重代入」と紛らわしいから。