JavaFX のシーケンスはキモいのか?

JavaFXでは配列やリストに相当するデータ型としてシーケンスがあります。一見、LLのリストのように配列オブジェクトで追加や削除などのメソッドが定義されているように思えますが、実態はJavaの配列に近く、記述方法がJSONっぽく書けるだけです。また、追加/削除などの操作はできず、insert文を使うことで新しいシーケンスを生成するような仕組みになっています。特にシーケンスへ要素を追加する場合、insert文を使うなど、かなり意味が解らない所です。

可変長配列も後ろに追加するのにこんな感じ。

var a = [];
insert "a" into a;
insert "b" into a;
println(a);

え゛!
なにこれ。COBOLをやりたいの?わけわかんねー。

http://d.hatena.ne.jp/shin/20090803/p1

JavaFX Scriptではシーケンスにinsert文やdelete文が使用できます。よくわかりませんが使ってみました。

http://d.hatena.ne.jp/shuji_w6e/20090730/1248969921

定義

シーケンスの定義は、[]で囲むだけなので非常に宣言的です。

var nums:Integer[] = [1, 2, 3, 4, 5];

また、範囲記法もサポートされています。

var nums:Integer[] = [1..5]; // [1, 2, 3, 4, 5] と等価

for記法を使う事でこんな書き方も可能です。

var nums:Integer[] = for(x in [1..5]) x * x; // [1, 4, 9, 16, 25]

シーケンスに要素を追加/削除する

厳密には新しいサイズのシーケンスが生成されるようですが、追加にはinsert文を使います。

insert 10 into nums; // [1, 2, 3, 4, 5, 10];

値を指定して前後に配置することもできます。

insert 10 before nums[1]; // [1, 10, 2, 3, 4, 5];
insert 10 after nums[1]; // [1, 2, 10, 3, 4, 5];

削除する場合はdelete文を使用します。

delete 2 from nums; // [1, 3, 4, 5];

はい、意味解りません。キモイと思います。


・・・そう思っていた時期がありました。

マウスオーバー時に画像の変換をかける

それではしっくりくる例を紹介します。
この円(Cirecle)はクリックすると一瞬凹むような、いわゆるボタン風なエフェクトになります。

def click_transration = Translate { x : 1.0, y : 1.0 };
var node = Circle {
    transforms: []
    onMousePressed: function(event: MouseEvent) {
        insert click_transration into node.transforms
    }
    onMouseReleased: function(event: MouseEvent) {
        delete click_transration from node.transforms
    }
}

Node(JavaFXのシーングラフにおけるオブジェクトの単位、グループ化もできる)の属性のひとつにtransformsがありますが、これはオブジェクトに回転や移動の変換をかける為のシーケンスです。画像処理をしたことのある人ならば解ると思いますが、移動・回転・拡大縮小などはすべて行列で表され、それを座標系に掛け合わせることで座標変換を行うのです。
ここでは単純な座標変換として、X方向とY方向にそれぞれ1ドット移動する変換click_transrationを定義しています。defはJavaでいうところのfinalだと思ってください(中身にも再代入できないので定数に近いです)。
この円(Circle)はマウスが押された時と離された時にそれぞれイベントを定義していますが、ポイントはclick_transrationをtransformsへ追加したり、取り除いたりしている所です。

一般的なLL風擬似コードで書いてみます。

def click_transration = Translate { x : 1.0, y : 1.0 };
var node = Circle {
    transforms: []
    onMousePressed: function(event: MouseEvent) {
        node.transforms.append(click_transration)
    }
    onMouseReleased: function(event: MouseEvent) {
        node.transforms.remove(click_transration)
    }
}

これでも十分に意味は通じるのですが、手続き的な記法になっている事に注意して、insert/deleteと比べてみてください。すると、宣言的に書くのであれば、insert/deleteの記法がしっくり来ることに気づくと思います*1。また、before/afterもある変換の前/後に別の変換を入れたい・・・というような状況で非常にわかりやすい記述になると感じます。

というわけでキモくない、かわいいです><

*1:プログラマが考えるとそうでもないと思うかもしれまんが・・・