spread の Operator Overloading
Groovy では List は * 演算子で 1つ外側の List の要素に展開できる。
groovy:000> [1,2,*[3,4,5]] ===> [1, 2, 3, 4, 5]
Groovy の演算子はメソッドで定義できるが http://groovy.codehaus.org/Operator+Overloading に * は載っていない。*1
groovy:000> [*new Object()] ERROR java.lang.IllegalArgumentException: cannot spread the type java.lang.Object with value java.lang.Object@5140709 at groovysh_evaluate.run (groovysh_evaluate:2)
groovysh でたずねるとメッセージがでるのでソースを検索してみる。
ScriptBytecodeAdapter.despreadList の該当箇所
Object value = spreads[spreadPos]; if (value == null) { ret.add(null); } else if (value instanceof List) { ret.addAll((List) value); } else if (value.getClass().isArray()) { ret.addAll(DefaultTypeTransformation.primitiveArrayToList(value)); } else { throw new IllegalArgumentException("cannot spread the type " + value.getClass().getName() + " with value " + value); }
展開演算子は null か List か 配列の場合に適用される。
タプル再び
Parser Combinator でタプルを定義したが元々 Groovy にタプルはあるみたいだ。
Groovyソースコード斜め読み(その1)「Groovyにもタプルがあった 」 - No Programming, No Life
@TupleConstructor はタプル風に呼び出せるコンストラクタってことだ。
Map 風もよいがタプル風に呼びたい場合もあるので再び定義する。
@groovy.transform.TupleConstructor(excludes="pair") class Pos { int x int y @Delegate List pair = [x, y] }
今回は多重代入だけでない。*2
@Newify([Pos]) def eval() { assert [1,2,3,4] == [*Pos(1,2), *Pos(3,4)] assert [Pos(1,2), Pos(3,4)] == [Pos(1,2), Pos(3,4)].collect { it } assert [Pos(2,1), Pos(4,3)] == [Pos(1,2), Pos(3,4)].collect { x, y -> new Pos(y, x) } assert [[1,1,1], [1,2,3]] == [Pos(1,1), Pos(1,2), Pos(1,3)].transpose() }
でも扱いが List なので println では toString を呼び出してくれない。
InvokerHelper
public static String toString(Object arguments) { if (arguments instanceof Object[]) return toArrayString((Object[]) arguments); if (arguments instanceof Collection) return toListString((Collection) arguments); if (arguments instanceof Map) return toMapString((Map) arguments); return format(arguments, false); }