2008年8月7日木曜日

JavaFX台本型プログラミング言語基準(草案):改訂版

以前に和訳を投稿した「The JavaFX™ Script Programming Language Reference (Draft)」が改訂されましたので、訳の方も改めました。改訂内容としては、単に記述項目を追加しただけでなく、以前の記述内容や表現の見直しも行われています。
-------------------------------------------

JavaFX台本型プログラミング言語基準(草案)

目次
1.言語概要
2. クラスとオブジェクト
3.属性と関数
4.変数と基本データ型
5.式と演算子
6.列を宣言する
7.列を利用する
8.データ結合
9.引き金
10.動画
A.鍵語と予約語



1.言語概要
この章では、JavaFX™台本型プログラミング言語(訳注:以下、「JavaFX言語」と略記)の概要について説明する。ここでは、このプログラミング言語の主な特徴を高い水準で述べ、特定の構成概念についての詳述は後の章で行う。この本は、ウェブ(Web)ページにおいて、Java™ Web Startソフトウェアとしてまたは従来型の机上応用プログラムとして実行される、表現力豊かなインタネット依頼機応用プログラムや要素の、設計者および開発者を対象としたものである。読者がJavaScriptまたはJava™プログラミング言語(訳注:以下、「Java言語」と略記)のいずれか、または両者に精通していることを想定している。この文書は公式な言語仕様を定めたものではないが、現在支援されている言語の特徴のすべてに対する完全な基準とみなすことができる。
JavaFX言語は、次の特徴を有している。

・図式利用者インタフェース(GUI)構成部品の指定に宣言型構文を
使用しており、開発者のプログラムコードをGUIの実際の配置に
ぴたりと対応させることを可能にしている。
・宣言型データ結合と増分評価を使用しており、個々の構成部品を
容易に作成および構成することを可能にしている。応用プログラムの
データとGUIの構成部品は、自動的に同期がとられる。
・静的に型付けされており、Java言語において大規模プログラムの
作成と保守を可能にしているのと同様のコード構造、再利用および
情報隠ぺいなどの特性のほとんどを有している。
・どのような規模または複雑さのGUIも支援することができる。
・Swingの利用を容易にする。

以下の節では、JavaFX言語について駆け足で紹介する。しかるべき点についてはJava言語と比較および対比させながら、その核となる構文と機能についての一般的な導入を与える。そして、それぞれの主題は後の章で更に詳しく取り上げられる。


台本
JavaFX言語において、「台本」は1つ以上の宣言または式である。台本を評価することは、宣言または式を順序を追って評価することである。

var ten : Integer = 10;
java.lang.System.out.println("Twice {ten} is {2 * ten}.");

これは、次のように表示する。

Twice 10 is 20.

Java言語で書かれた応用プログラムと異なり、台本は、いかなるクラス宣言または関数も含む必要がない。


クラス
クラスの定義は、Java言語と多くの共通点を持っているが、いくつかの相違点は注目に値する。たとえば、情報は、領域ではなく、属性に格納される。振る舞いは、操作ではなく、関数によってあらわにされる。次の例は、それぞれの基本構文を示す単純な矩形クラスを定義している。

class Rectangle {

attribute width: Integer;
attribute height: Integer;

function grow(): Void {
grow(1);
}

function grow(amount: Integer): Void {
width += amount;
height += amount;
}

}

JavaFX言語は多重継承を支援しており、必要なら複数のクラスを継承することができる。
クラスは、第2章で詳しく取り上げられる。
属性と関数は、第3章で詳しく取り上げられる。


オブジェクト
オブジェクト直定数は、クラスの実体化のための簡便な構文を提供する。次のコードは、前述した矩形クラスの単一の実体を生成し、その幅と高さの属性を100に初期化する(newを必要としない点に留意すること)。

Rectangle {

width: 100
height: 100

}

このオブジェクトへの参照を格納するには、var鍵語を使う。

var myRect = Rectangle {

width: 100
height: 100

}

オブジェクトは、第2章で詳しく取り上げられる。
変数と基本データ型は、第4章で詳しく取り上げられる。


式と演算子
他のプログラミング言語と同様に、JavaFX言語は、式と演算子を支援する。
第5章は、JavaFX言語において利用可能な式と演算子について論じる。



列は、オブジェクトの順序付けられた一覧を保持する。これは、Java言語の配列にほぼ類似している。両方とも複数の値を保持し、0から始まる索引により検索される。

var week = ["Monday","Tuesday","Wednesday","Thursday",
"Friday","Saturday","Sunday"];
var mon = week[0];
var wed = week[2];
var fri = week[4];

部分列も支援されている。

var week = ["Monday","Tuesday","Wednesday","Thursday",
"Friday","Saturday","Sunday"];
var weekdays = week[0..4]; // 第1部分列
var weekend = week[5..6]; // 第2部分列

第6章は列の宣言の基本を取り上げており、一方、第7章は列の利用に焦点を当てている。


データ結合
データ結合は、複数オブジェクトの状態を同期させるための簡便な構文を提供する。2つのオブジェクトが互いに結合されると、1番目のオブジェクトが更新されたときはいつも2番目のオブジェクトの値が自動的に変更される。データ結合の一般的な利用法は、GUI部品をその基となるデータに同期させ続けることである。

import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.text.Text;

var myString = "Hello World!";

Frame {
width: 50
height: 50
visible: true
stage: Stage {
content: Text {
content: bind myString
}
}
}

// コードのほかの部分でmyStringを変更すると、
// GUIの文字列も自動的に変更される。

データ結合は、第8章で詳しく取り上げられる。


引き金
引き金は、ある条件が満たされると実行されるコードの区画である。たとえば、ある属性の値が不適切なものに設定された場合には、注意を喚起してほしいと思うかもしれない。次の例は、その基本的な引き金の構文を示す。

import java.lang.System;

ReplaceDemo {

mySensitiveData: "Will anyone notice?"

}

class ReplaceDemo {
attribute mySensitiveData: String
on replace {
System.out.println("I noticed a change!");
};

// 応用特有の保護手段コードがここに書かれる
}

引き金は、第9章で詳しく取り上げられる。



2.クラスとオブジェクト

クラスを取り込む
取り込み文は、Java言語と同様に機能する。その構文は、

import パッケージ名.クラス名;

および

import パッケージ名.*;

である。
取り込み文が存在する場合は、それ以外のすべての応用コードの前になければならない。JavaFX言語は、自身の組み込みライブラリクラスのための名前空間(パッケージjavafx.*)を定義しているが、標準的なJava言語のクラスを取り込むこともできる。

import javafx.application.*;
import java.lang.System;

...


クラスを定義する
クラスを指定するための構文は、class鍵語に続けたクラス名、任意選択としてのextends鍵語とカンマで区切った基底クラス名の一覧、左中括弧、それぞれセミコロンで終結している属性と関数の一覧、および右中括弧からなる。
JavaFX言語は多重継承を支援しており、そうしたものとして、新たな用語と規定を定義している。

・単純クラスは、Java言語で書かれたクラスを直接的または間接的に
拡張した任意のクラスである。
・複合クラスは、単純クラスに当てはまらないすべてのクラスである。
・既定では、JavaFX言語で書かれたクラスは複合クラスである。
・クラスは、1つの単純クラスだけを拡張することができる。
そうしたクラスは、単純クラスとなる。クラスはまた、任意の数の
複合クラスまたはJava言語のインタフェースを拡張することもできる。

注記:現在のところ、単純クラスはJavaクラスに変換され、一方、複合クラスはJavaクラスとJavaインタフェースに変換される。

クラスはpublicと宣言することができ、それは、そのクラスが台本のどこからでも利用できることを意味する。さもなければ、そのクラスを含んでいる台本からのみ利用できる(これが既定)。
JavaFX言語は構築子を支援していない。その代わりに、オブジェクト直定数を使用しなければならない。構築子の作用をまねるためには、新たなオブジェクトを返す関数を定義し、その関数を呼び出す。


オブジェクトを定義する
前の章で述べたように、クラス実体化の推奨方法はオブジェクト直定数を使うことである。この形式のオブジェクト割り当ては、クラス名とその後に続く中括弧でくくった属性初期化子の一覧から構成される宣言的構文を用いる。それぞれの初期化子は、属性名に、コロンとその属性の値を定義する式を続けて構成する。しかしながら、オブジェクトを生成する際にnew鍵語を使うこともできる。

import java.io.File;

var tmpPath = "/home/users/docs/tmp.txt"
var myFile = new File("tmp.txt");



3.属性と関数

属性
属性はオブジェクトの状態を定める。それらは、attribute鍵語に、属性名、コロン(任意選択)、属性の型(任意選択)を続け、セミコロンで終結させたものを用いて宣言される。

attribute 属性名 : 属性型 ;

属性の既定値を宣言することができる(後で、オブジェクト直定数で規定される値に定義しなおすことができる)。初期化子は、新たに生成されるオブジェクトの状況に即して、クラス宣言内で属性が指定された順に評価される。

import java.lang.System;

class X {
attribute a: Number = 10;
attribute b: Number = -1;
}

var x = X { };
System.out.println(x.a); // 10.0を表示する
System.out.println(x.b); // -1.0を表示する

明示的な初期化が行われないと、それぞれの属性は妥当な既定値に設定される。

import java.lang.System;

class DefaultValuesDemo {
attribute a: Number;
attribute b: Integer;
attribute c: Boolean;
attribute d: String;
}

var demo = DefaultValuesDemo {};
System.out.println("Default Value: " + demo.a);
System.out.println("Default Value: " + demo.b);
System.out.println("Default Value: " + demo.c);
System.out.println("Default Value: " + demo.d);

上記コードは、次の既定値を画面に表示する(注記:最後の行は、空文字列を表示する)。

Default Value: 0.0
Default Value: 0
Default Value: false
Default Value:

データ型は、第4章で取り上げられている。


関数
関数はオブジェクトの振る舞いを定める。関数は次の形式を取る。

function 関数名 (属性名 : 属性型, ...): 戻り型 本体

ここで、本体は任意の式でかまわない。
関数は第一級オブジェクトである(たとえば、変数に割り当てたり、引数としてほかの関数へ渡すことができる)。
第1章で、何も引数を取らず、そして何も値を返さないgrow関数を定義している簡単な例を示した。

...

function grow(): Void {
grow(1);
}

...

その例証では、利用者が特定の寸法を指定できるようにした、関数の多重定義版も与えた。

...

function grow(amount: Integer): Void {
width += amount;
height += amount;
}

...

関数は、無名関数にもなりうる。無名関数は、GUI構成部品のaction属性に振る舞いを割り当てる際に、しばしば利用される。

// GUIにおいて

...

action: function() {
System.out.println("Click!");
}

...



4.変数と基本データ型

変数
第1章で論じたように、var鍵語は、新たな変数をプログラムに導入するために用いられる。変数の型は、それを宣言する際に指定してもよいが、指定は任意選択である。宣言から変数の型が省略されると、その型は変数の用いられ方からを推測することができる。
変数宣言は、次の形式を取る。

var 変数名 : 型 = 初期化子;

例:

var num = 1; // 推測された型
var num : Number = 2;
var firstName = "John"; // 推測された型
var lastName : String = "Doe";

変数の命名法の慣例は、Java言語における慣例と同一である。クラス名は、各単語の最初の文字を大文字にする(MyClass)。関数名は小文字で始まるが、後に続く各単語の最初の文字は大文字にする(myFunctionName)。定数は、下線文字で区切られた単語により、すべて大文字で表されなければならない(MY_CONSTANT)。
二重山括弧<<>>で閉じられた(空白文字を含む)いかなる文字列も、識別子として扱われる。これにより、JavaFX言語の鍵語(または、ほかの通常では不正な識別子)を、クラス、変数、関数、または属性の名前として用いることができる。
例:

var <<delete>> = 100;

また、JavaFX言語の鍵語と同じ名前の(Java言語で書かれた)操作を呼び出すこともできる。

import javax.swing.JTextArea;

var textArea = new JTextArea();
textArea.<<insert>>("Hello", 0);

変数の存続期間は、少なくとも、それを含むコード区画の存続期間と同じである。


基本データ型
JavaFX言語では、原始型(プリミティブ型)という用語は使わない。代わりに、5つの基本データ型を定義しており、応用コードで常に利用することができる。
5つの基本データ型は、次のようにJava言語に対応付けられる。

表 4.1 基本データ型
JavaFX言語 Java言語
String java.lang.String
Boolean java.lang.Boolean
Number java.lang.Number
Integer byte,short,int,long,BigInteger
Duration 該当なし

最初の4つのデータ型は、Java言語でもしばしば使われるので、ほとんどの開発者には既になじみのあるものであろう。しかし、Duration型はこれまでになく、JavaFX言語に特有のものである。javafx.lang.Durationクラスは、時間の単位(ミリ秒、秒、分、または時間)を表す。Durationクラスを実体化するための簡略表現である、時間直定数も支援している。

5ms; // 5ミリ秒
10s; // 10秒
30m; // 30分
1h; // 1時間

整数を表す型については、引数または戻り値をJava言語で書かれた操作と受け渡しするときに、自動的に強制型変換が行われる。
文字列に関しては、一重引用符または二重引用符のいずれかを用いて文字列直定数を指定することができる。

var s1 = 'Hello';
var s2 = "Hello";

一重引用符と二重引用符は対称性を持っている。一重引用符を二重引用符の中に埋め込むことができるし、二重引用符を一重引用符の中に埋め込むこともできる。どちらにも、中括弧「{}」を用いて式を埋め込むことができる。

var name = 'Joe';
var s = "Hello {name}"; // s = 'Hello Joe'

埋め込まれた式自体が引用符で囲まれた文字列を含むことができるので、次々に、さらなる埋め込み式を含むことができる。

var answer = true;
var s = "The answer is {if (answer) "Yes" else "No"}";
// s = 'The answer is Yes'

Java言語と異なり、JavaFX言語の文字列直定数は、改行文字を含むことができる。
文字列式に付加的な書式設定接頭辞を与えることにより、数値と日付をどのように文字列に変換するかについても制御することができる。この付加的な書式設定接頭辞は、java.util.Formatterの仕様に従う。

import java.util.Date;

var hexStr = "hex of 13 is 0x{%02X 13}";
// hexStr = "hex of 13 is 0x0D"
var date = new Date(107, 10, 11);
var dateStr = "{%tc date}";
// dateStr = "日 11 11 00:00:00 JST 2007"

訳注:原文では、「var dateStr = "{%tc date}";」文末のセミコロン(;)が抜け落ちている。また、日本では、日付は上記コメントの書式で表示される。



5.式と演算子


Java言語と異なり、JavaFX言語は式言語である。すべての実行文は、0個以上の入力とそれに続く0個(または1個)の出力から構成される式である。これには、条件、繰り返し、およびコード区画も含まれる。
次に実例を示す。

import java.lang.Math;
import java.lang.System;

var rand = (Math.random() * 100).intValue();
var s:String = null;
if (rand mod 2 == 0) {
s = "rand is even"
} else {
s = "rand is odd"
};

System.out.println(s);

訳注:原文では、剰余演算子として「%」を使用しているが、表 5.1に載っているように、これは「mod」に変わっている。したがって、上記のコード例もmodで書き換えている。


区画式
区画式は、中括弧で囲まれた、セミコロン区切り文(宣言または式でもよい)の羅列から構成される。(区画内の)最後の文が式なら、その式の値が区画式の値となる。さもなければ、区画式はvoid型となる。
次の区画式は、数個の数を加え、その結果をtotalという名前の変数に格納する。

import java.lang.System;

var nums = [5, 7, 3, 9];
var total = {
var sum = 0;
for (a in nums) { sum += a };
sum;
}

System.out.println("Total is {total} ");


値域式
値域式は、次の構文により、等差数列を形成している数値の列を定める。

[数1..数2]

上記の式は、数1から数2までの整数(両端含む)からなる列を定める。
値域式の簡単な例は、次のようなものであろう。

import java.lang.System;

var nums = [0..3];
System.out.println(nums == [0,1,2,3]); // trueと表示する

既定では各数値間の刻み幅は1だが、異なる刻み幅を指定することもできる。たとえば、次の式は、1から10の間の奇数からなる列を定める。

[1..10 step 2]

降順に並んだ値域を生成するためは、終了値が開始値より小さいことを確認し、負数の刻み幅を指定する。

import java.lang.System;

var nums = [3..0 step -1];
System.out.println(nums == [3,2,1,0]); // trueと表示する

次の宣言文は、実際には空の列を宣言することに留意せよ。

var nums1 = [3..0 ];
var nums2 = [3..0 step 1];


条件式
if式は、Java言語におけるifと似ている。

if (condition1) {
System.out.println("Condition 1");
} else if (condition2) {
System.out.println("Condition2");
} else {
System.out.println("not Condition 1 or Condition 2");
}

Java言語は、if式と条件式(たとえば、a < b ? a : b)の両方を含んでいる。JavaFX言語のif式は、その両者に代わるものである。


繰り返し式
for式
for式は列とともに用いられるので、第7章で論じる。

while式
while式はJava言語の場合と同様であるが、whileの本体を囲む中括弧が常に必要である。
例:

import java.lang.System;

var i = 0;
while (i < 10) {
if (i > 5) {
break;
}
System.out.println("i = {i}");
i += 1;
}


そのほかの式
return式は、Java言語において見られるものと同一である。

function add(x, y) {
return x + y;
}

throw式は、Java言語のものと似ているが、java.lang.Throwableを拡張したオブジェクトだけが投げられたり捕捉されたりできる。

import java.lang.Exception;

function foo() {
throw new Exception("this is a java exception");
}

function bar() {
throw "just a string";
}

try式とcatch式は、Java言語のものと似ているが、JavaFX言語の変数宣言の構文を用いる。

try {
throw "Hello";
} catch (s:String) {
System.out.println("caught a String: {s}");
} catch (any) {
System.out.println("caught something not a String: {any}");
} finally {
System.out.println("finally...");
}

break式とcontinue式は、Java言語のものと似ているが、ラベルは支援されていない。
例:

import java.lang.System;

function foo() {
for (i in [0..10]) {
if (i > 5) {
break;
}
if (i mod 2 == 0) {
continue;
}
System.out.println(i);
}
}

function bar() {
var i = 0;
while (i < 10) {
if (i > 5) {
break;
}
if (i mod 2 == 0) {
continue;
}
System.out.println(i);
i += 1;
}
}


演算子
JavaFX言語は、Java言語にみられるものと同様の標準的な演算子を提供する。次の表は、Java言語における等価演算子と比較させながら、優先順位に従ってそれらの演算子を一覧表にしたものである。

表 5.1 演算子の優先順位
優先度 JavaFX演算子 演算 Java演算子 評価順序
1 function() JavaFX関数
() 括弧内の式
new オブジェクト生成
{オブジェクト オブジェクト生成
直定数} と初期化
2 ++ (後置) 1増後代入 ++ 右から左
-- (後置) 1減後代入 --
3 ++ (前置) 1増前代入 ++ 右から左
-- (前置) 1減前代入 --
not 論理否定
sizeof 列の要素数
reverse 列の反転
indexof 列要素の索引


4 * 乗算 * 左から右
/ 除算 /
mod 剰余 %
5 + 加算 + 左から右
- 減算 -
6 == 等価 == 左から右
!= 不等価 !=
< 小なり <
<= 以下 <=
> 大なり >
>= 以上 >=
7 instanceof 型検査 instanceof
as 型変換
8 or 論理OR || 右から左
9 and 論理AND && 右から左
10 += 加算代入 +=
-= 減算代入 -+
*= 乗算代入 *=
/= 除算代入 /=
%= 剰余代入 %=
11 = 代入 =

例:

import java.lang.System;
import java.lang.Math;

var x = 2;
var y = 4;
var a = true;
var b = false;
System.out.println(x == y); // falseを表示する
System.out.println(x != y); // trueを表示する
System.out.println(x < y); // trueを表示する
System.out.println(x > y); // falseを表示する
System.out.println(x >= y); // falseを表示する
System.out.println(x <= y); // trueを表示する
System.out.println(x + y); // 6を表示する
System.out.println(x - y); // -2を表示する
System.out.println(x * y); // 8を表示する
System.out.println(x / y); // 0を表示する
System.out.println(x mod y); // 2を表示する
System.out.println(a and b); // falseを表示する
System.out.println(a or b); // trueを表示する
System.out.println(not a); // falseを表示する
System.out.println(sizeof [x,y]); // 2 を表示する
System.out.println([x,y][e | indexof e == 0]); // [ 2 ]を表示する
System.out.println(if (a) x else y); // 2を表示する
System.out.println(for(q in [x, y] where q < 3) q); // [ 2 ]を表示する
System.out.println(Math.max(x, y)); // 4を表示する
System.out.println("abc".toUpperCase()); // ABCを表示する
System.out.println(x); // 2を表示する

訳注:原文で使用している「<>」演算子と「%」演算子は、それぞれ、「!=」と「mod」に書き換えた。



6.列を宣言する
先に述べた5つの基本データ型に加え、JavaFX言語は、「列」として知られたデータ構造を提供する。列はJava言語の配列に似ているが、異なる点もある。
次のコードはいくつかの例を示す。

var weekDays = ["Mon","Tue","Wed","Thur","Fri"];
var days = [weekDays, ["Sat","Sun"]];

列は、オブジェクトの順序付けられた一覧を表す。しかしながら、列そのものはオブジェクトではないため、入れ子構造を取ることはできない。列は、値により等価であるとみなされる。列の長さが等しく、かつそれらの要素が同一であるとき、列は等しい。(上記のdaysの初期化におけるように)入れ子になった列を形成する式は、自動的に平準化される。

days == ["Mon","Tue","Wed","Thur","Fri","Sat","Sun"]; // trueを返す

さらに、単一のオブジェクトは、1つのオブジェクトからなる列に等価である。

1 == [1]; // trueを返す

列の型は、「[]」注釈を用いて宣言される。

var xs:Number[]; // 数の列
var strs:String[]; // 文字列の列

列の要素は共通の型を持たねばならず、その型はオブジェクトであっても良い。列は、Java言語の配列のように、索引付けることがきる。

var wednesday = days[2];

次の例のように、その要素が等差数列をなしている列に対する「..」を用いた簡便な表記法もある。

var nums = [1..100];

この簡略記法により、各要素を手入力する必要がなくなる。
[]演算子は、述語形式の選択も表す。述語は、次の形式を取る。

列[変数名| 論理式]

例:

var nums = [1,2,3,4];
var numsGreaterThanTwo = nums[n|n > 2];

この手の式は、元の列の要素の内、述部の条件を満たすものから構成される新たな列を返す。
最後になったが、部分列は、列の一部を利用できるようにする。

seq[a..b] // 索引aとbの間(両端含む)の列
seq[a..<b] // 索引a(含む)とb(含まない)の間の列
seq[a..] // seq[a..<sizeof seq]に同じ
seq[a..<] // 一貫性を保つため、seq[a..<sizeof seq-1] に同じ

次の章で述べるように、いくつもの異なる方法で、列または部分列を利用および/または変更することができる。



7.列を利用する

forを用いた列の繰り返し
JavaFX言語は、for演算子を用いた列内包を支援する。列内包は、1つ以上の入力列、任意選択としてのろ過器、および式から構成される。各入力列は変数と関連している。列内包の結果は、元となる列の要素の組み合わせの内で、ろ過器の条件を満たすものに、式を適用した結果であるところの新たな列である。
次のプログラムはこの構文の実例を示したもので、曲集の一覧から曲の一部が表題となっているものを特定するためにfor演算子を用いている。

class Album {
attribute title: String;
attribute artist: String;
attribute tracks: String[];
}

var albums =
[Album {
title: "A Hard Day's Night"
artist: "The Beatles"
tracks:
["A Hard Day's Night",
"I Should Have Known Better",
"If I Fell",
"I'm Happy Just To Dance With You",
"And I Love Her",
"Tell Me Why",
"Can't Buy Me Love",
"Any Time At All",
"I'll Cry Instead",
"Things We Said Today",
"When I Get Home",
"You Can't Do That"]
},
Album {
title: "Circle Of Love"
artist: "Steve Miller Band"
tracks:
["Heart Like A Wheel",
"Get On Home",
"Baby Wanna Dance",
"Circle Of Love",
"Macho City"]
}];

for (album in albums, track in album.tracks) {
if (album.title == track)
java.lang.System.out.println("TITLE TRACK = {track}")
else
java.lang.System.out.println("Track = {track}")
}

出力:
TITLE TRACK = A Hard Day's Night
Track = I Should Have Known Better
Track = If I Fell
Track = I'm Happy Just To Dance With You
Track = And I Love Her
Track = Tell Me Why
Track = Can't Buy Me Love
Track = Any Time At All
Track = I'll Cry Instead
Track = Things We Said Today
Track = When I Get Home
Track = You Can't Do That
Track = Heart Like A Wheel
Track = Get On Home
Track = Baby Wanna Dance
TITLE TRACK = Circle Of Love
Track = Macho City

次に示すのは、ろ過器を用いた別の例である。これは、数を受け取ってその因数の一覧を返す関数を定義したものである。

function factors(n:Number) {
return for (i in [1 .. n/2] where n mod i == 0) i;
}

訳注:原文の「%」演算子を、「mod」に書き換えた。

forの内包の中で、indexof演算子を用いることができる。その構文は「indexof 名称」で、「名称」は繰り返し変数の名前である。その値は、基となる列における繰り返しの「索引」値である。
次の例に示したように、indexof演算子は列の抽出に使用することもできる。

var nums = [1..5];
var numsExceptTheFirstTwo = nums[n|indexof n > 1]; // [ 3, 4, 5 ]を返す


列変数を変更する
insertは、列に新たな要素を挿入する。

insert x into seq
insert x before seq[idx]
insert x after seq[idx]

deleteは、列から要素を取り除く。

delete seq
delete x from seq
delete seq[idx]
delete seq[a..b]

次のコードは、挿入と削除の双方の例である。

// 挿入の例

var nums = [1..5];
var x = 6;
insert x into nums; // 結果は、[ 1, 2, 3, 4, 5, 6 ]
x++;
insert x before nums[0]; // 結果は、[ 7, 1, 2, 3, 4, 5, 6 ]
x++;
insert x after nums[3]; // 結果は、[ 7, 1, 2, 3, 8, 4, 5, 6 ]

// 削除の例

nums = [1..5];
delete 2 from nums; // 結果は、[ 1, 3, 4, 5 ]
delete nums[0];// 結果は、[ 3, 4, 5 ]
nums = [1..10]; // 結果は、[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
delete nums[3..7]; // 結果は、[ 1, 2, 3, 9, 10 ]
delete nums; // 結果は、[ ]
nums = [1..10];
delete nums[5..]; // 結果は、[ 1, 2, 3, 4, 5 ]
delete nums[0..<]; // 結果は、[ 5 ]

訳注:原文では、最後の「delete nums[0..<];」文が「delete nums[0..>];」と誤記されている。

列の要素を逆順に並べ替えることもできる。

var nums = [1..5];
reverse nums; // [ 5, 4, 3, 2, 1 ]を返す

訳注:2008年7月23日時点のNetBeans 6.1用JavaFX追加接続(Plugin)プログラムで動作確認したところ、「reverse nums;」だけでは、元のnums の要素の並びは変わらなかった(並び替えた結果を利用するためには、「nums = reverse nums;」のように列変数に代入しなおす必要がある。その意味では、delete演算子とは用法が異なる)。



8.データ結合
bind鍵語は、対象となる変数の値を別の変数の値に結びつける。

let x = bind someExpression;

これは、someExpressionの結果を変数xに結びつける。someExpressionの値が変わると、xの値も自動的に更新される。結び付けられる変数は、ある基本データ型の単純な値、式の結果、区画式の結果、もしくは結合関数でもよい(let鍵語は、それらに割り当てることができない変数を導入する)。


再計算して更新する
ある状況では、更新がどのように行われるか、あるいはsomeExpressionが変化することはどういう意味なのかを厳密に知っている必要ある。結び付けられる値が変化すると、最小限の再計算が実行される。この再計算は、ある限られた状況において問題になるだけである。たとえば、オブジェクトの生成を必要とする結合が実行される場合で、オブジェクトの同一性の理由から、新たなオブジェクトが実際に生成されたかどうかが問題となるような場合である。

let sum = bind expr1 + expr2;

expr2が変化するとsumは再計算されるが、expr1は再計算されない。expr1に対する値はすでに格納されており、単にその値を再度取ってくるだけである。

次に実例を示す。

import java.lang.System;

var y = 3;
function ten() : Integer { 10 }
let sum = bind ten() + y;
System.out.println(sum);
y = 7;
System.out.println(sum);

このプログラムの出力は次のようになる。

13
17

yが7に設定されたとき、関数ten()は変化していないので再度呼び出されることはない。関数値は記憶されており再利用される。


結合と区画式
bindにおいて、区画式の中に存在しうる文は変数宣言だけである。bind内では、(1増および1減を含め)値の割り当ては禁止されていることに留意せよ。したがって、結合区画式は次のような形式を取る。

bind { var a = expr; var b = expr; var c = expr; expr }

結合された式に対するいかなる変化も更新を引き起こし、その更新は最小限なので、変数は効率的に結合される。while、insertおよびdeleteは式なので、bindの中に現れてはならないことに留意せよ。


結合と条件式
次の例を考えてみよ。

let x = bind if (condExpr) expr1 else expr2;

condExprが変わると、if式のどちらの分岐が評価されるのかが変わる。expr1またはexpr2に対する変化は、どちらも、他方の式の再計算を引き起こすことはない。


結合とfor式
次の例を考えてみよ。

let newSeq = bind for (elem in seq) expr;

もしseqが変わっても、それでもseqに残っている要素に対応するnewSeqの要素は、再計算されない。言い換えれば、要素がseqに挿入されると、その要素にexprを適用した結果がnewSeqの相当する位置に挿入され、newSeqのほかの要素は再計算されない。
この例外は、exprがindexof elemを使用しているために、索引の値が変化した要素を更新する必要がある場合であるが、最小限の更新という規定には合致している。

import java.lang.System;

var min = 0;
var max = 3;
function square(x : Integer) : Integer { x*x }
let values = bind for (x in [min..max]) square(x);
System.out.println(values);
max = 5;
System.out.println(values);
min = 1;
System.out.println(values);
min = 0;
System.out.println(values);

この場合、次の再計算が実行される。

・0から3の2乗を計算する。
・4と5の2乗を計算する(maxが変わっても、0から3の2乗は再計算されない)。
・ほかの値は再計算せず、0の2乗を削除する。
・0の2乗を戻す。これは、再計算を必要とする。
この動作は、代わりにinsertやdeleteを用いても同様である。

結合とオブジェクト直定数
オブジェクト直定数は、演算子や非結合関数のように機能する。オブジェクト直定数の引数の

let pt = bind Point { x: myX y: myY }

myXが変わると、JavaFX言語は、新たなPointオブジェクトを生成する。これは、不変オブジェクトに対して予期された振る舞いでもある。
新たなオブジェクトを生成せずに、xの値がmyXの値を追跡するようにするためには、結合が必要になる。

let pt = bind Point { x: bind myX y: myY }

この例に対しては、おそらくyも同様に結合したいと思うだろう。

let pt = bind Point { x: bind myX y: bind myY }

ここで、ptは常に同じPointオブジェクトの実体と結合したままである。それは、最初の結合がなくても同様である。

let pt = Point { x: bind myX y: bind myY }


結合と関数
非結合関数は、bound鍵語が前置されていない関数である。Java言語の操作やJavaFX言語の関数の呼び出しに関して、その引数のいずれかが変更されると、JavaFX言語はその関数を再度呼び出すが、関数の本体は暗箱である。関数が、その入力となる引数以外に持っている依存性により再計算が引き起こされることはない。

import java.lang.System;

class Point {
attribute x : Number;
attribute y : Number;
}

var scale = 1.0;

function makePoint(x0 : Number, y0 : Number) : Point {
Point {
x: x0 * scale
y: y0 * scale
}
}

var myX = 3.0;
var myY = 3.0;
let pt = bind makePoint(myX, myY);
System.out.println(pt.x);
myX = 10.0;
System.out.println(pt.x);
scale = 2.0;
System.out.println(pt.x);

出力:

3.0
10.0
10.0

引数myXを変更すると、関数makePointは再度呼び出される。しかし、関数makePointは暗箱である。よって、scaleに割り当てられた値を変更しても、当然ではあろうが、関数は再計算されない。これこそが、結合関数が意図したものである。結合関数は予期された更新を引き起こす。
結合関数は、その本体として、結合された区画式を持っている(それゆえに、結合区画式に関する上述の制限を有している)。結合関数に結びつけると、引数以外の変化も更新を引き起こし、かつ引数の変更も調べられる。上述の関数makePointは結合関数にしてもよい。

bound function makePoint(x0 : Number, y0 : Number) : Point {

そうすれば、scaleに割り当てられた値の変化は更新(20.0)を引き起こす。myXが変わるとx0 * scaleだけが再計算され、y0 * scaleは再計算されないことに留意せよ。
結合とは別のところで結合関数を呼び出すことは、非結合関数を呼び出すことと同じことである。



9.引き金

置換引き金
置換引き金に対する構文は次のとおりである。

"on" "replace"
[旧値]
[ "[" 最低索引値 ".." 最高索引値 "]" "=" 新要素 ]
区画

置換引き金は、引き金を取り付けた変数に対するすべての変更の後に呼び出される。構文上の非終端子である旧値、最低索引値、最高索引値、および新要素は、その本体が区画から成る関数に対する事実上の仮引数である。

・旧値は変数の事前の値であり、変数と同じ型を持つ。
・最低索引値と最高索引値は、旧値の置換対象となる部分の範囲を
区切る。それらの型はIntegerである。純然たる挿入に対しては、
「最高索引値==最低索引値-1」である。
・新要素は、部分列「旧値[最低索引値..最高索引値]」を
置き換える値の列である。その型は変数と同じである。

引数「[最低索引値..最高索引値]=新要素」は、引き金が取り付けられた変数が列型であるときのみ許される。
旧値と新要素は非対称であることに留意せよ。旧値は、引き金が取り付けられた変数の事前の値であり、一方、新要素は、変更対象となる要素のみから成る、その変数の新値の部分列である。
置換引金の構文は、部分列の割り当てを思い起こさせるようになっている。たとえば:

attribute x
on replace oldVal[lo..hi]=newVals { exp };
var save = x;
x[i..j] = y;

すると、oldVal はsaveに、loはiに、hiはjに、そしてnewValsはyに結びつけられた上で、expが評価される。
統合置換引き金は、部分列の割り当てとうまく機能する。たとえば、次のように結合を定義することができる。

attribute x;
attribute y = bind x;

これは次と等価である。

attribute x =
on replace [i..j]=n
{ y[i..j]=n };
attribute y = [];

次のように、yがxの写像だとする。

attribute x;
attribute y = bind for (xi in x) f(xi);

これは次と等価である。

attribute x =
on replace [i..j]=n
{ y[i..j] = for (k in n) f(k) };
attribute y = [];

列の要素のある範囲を削除または置換すること、もしくは列を一箇所に挿入することは、すべて単一の引き金呼び出しに帰着する。述語条件を満たすすべての要素の削除のような、別の演算のあるものは、複数の引き金呼び出しに分解できるかもしれない。その場合、それぞれの引き金呼び出しから見える状態は一致している。特に、プログラマに可視の状態は、あたかも(たとえば)述語削除が独立な部分列削除演算の組として実装されているかのようである。



10.動画

動画概要
JavaFXは、主こま(齣)動画(key frame animation)を支援する。これは、プログラマが、各場面の動画による状態遷移を、時系列上のある点における状態の断面(主こま)を宣言することにより表現する宣言様式である。JavaFX言語は、主こま動画の2つの基本的な種類、離散と補間を支援する。その違いは、後者では、専用の補間関数がそれぞれの動画のこま間に存在する状態を計算する。どちらの場合も、処理系は自動的に動画を実行し、要求に従って動画を停止、中断、再開、反転、または繰り返す。
高次では、主こま動画は次のように表現することができる。

・動画は、javafx.animation.Timelineオブジェクトにより表される
時系列に沿って再生される。
・それぞれの時系列は、javafx.animation.KeyFrameオブジェクト
により表される2つ以上の主こまを含む。
・それぞれの時系列は、それを表現するある種の属性(autoReverse、
repeatCount、toggle、など)と、再生を制御するいくつかの関数
(start()、stop()、pause()、およびresume())を提供する。
・それぞれの主こまには、それを含んでいる時系列における相対時刻が
付随し、それ自身、3つのもの、主値の一覧、従時系列、および
「引き金」を含む。含まれている従時系列のそれぞれは、主こまの
主時刻からの相対開始点で評価される。それゆえ、時系列は、
階層的に構成し、従時系列を含むことができる。従時系列自身、
同じことを繰り返すことができる。「引き金」は、それが存在すれば、
主こまで指定された時刻に実行される手続きコードの区画である。
・javafx.animation.KeyValueオブジェクトは、ある特性に対し、
その前の主こまからの相対で「中割り」の値を計算するために
用いられる関数とともに、主こまのその時刻におけるその特性の
最終状態を表す。

注記:Timelineオブジェクトは、図式動画とは無関係な、汎用の時間に基づく演算に対しても使用することできる。


離散動画
離散動画では、特定の性質の値が、主こまの時刻に、主こまで与えられた値に瞬時に変化する。たとえば、よく知られた「転げ回るデューク」の動画を考えてみよう。そこでは、動きの錯覚が、一組の画像を単にパラパラめくりすることによりもたらされる。こうした動画は、それぞれの動画のこまの「中間」を計算しないので、「離散的」である。

import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.paint.Color;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;

// 画像データを読み込む
var images = for (i in [1..17]) {
Image { url: "file:images/T{i}.gif"};
// 画像は手元に格納されていると仮定する

// ウェブから画像を読み込むには、代わりに次の行
// (訳注:原文では1行)を使用する
// Image {url: "http://java.sun.com/applets/other/
// TumblingDuke/images/T{i}.gif"};

}

// 最初の画像を設定する
var currDuke = images[0];


// 画像を繰り返すための時系列を生成する
var timeline = Timeline {
toggle: true
keyFrames: for (image in images) {
KeyFrame {
time: 100ms* indexof image
// 各こまは100ms離れる
action: function(){currDuke=image;}
}
}
}

Frame {

title: "Click on Duke!"
width: 250
height: 150
visible: true
stage: Stage {
content: ImageView {
image: bind currDuke;
onMouseClicked: function(e) {timeline.start();}
}
}
}

最初に、このプログラムは画像を記憶装置に読み込み、現在の画像を表すための変数(currDuke)を生成する。次に、時系列を生成し、いくつかの主こま(画像当たり1つ)を設定する。それぞれの主こまは100 msずつ離れて配置され、その画像をcurrDukeに割り当てる。最後に、GUIはその表示画像をcurrDukeと結合し、応用プログラムが動画のこまを繰り返すにつれて、現在の画像が画面上に表示されることを保証する。


補間動画
動画には、動的に計算しなければならない状態を持っているものもある。次に取り上げる例は、二次元の矩形をその寸法を変えながら回転させ、かつある範囲の表示色を繰り返す。この動画は、動画の経路に沿ったすべての可能な位置に主こまを生成する代わりに、プログラマが数個の主こま(開始、中間、終了)を単に宣言し、処理系にその中間のすべてを計算させるので、補間であるとされる。

import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.geometry.Rectangle;
import javafx.scene.paint.Color;
import javafx.scene.transform.Transform;
import javafx.scene.Cursor;
import javafx.animation.Timeline;
import javafx.animation.Interpolator;

Frame {
title: "Animation Demo";
width: 500
height: 400
visible: true
stage: Stage {
content: Rectangle {

// 変数を宣言する
var rotation = 0.0;
var size = 1.0;
var color = Color.GREEN;

// 属性を初期化する
cursor: Cursor.HAND
height: 50
width: 50
fill: bind color
transform: bind [Transform.translate(100,100),
Transform.scale(size,size),
Transform.rotate(rotation,25,25)]

// 動画の時系列を生成する
var myTimeline = Timeline {

toggle: true

// 主こま
var begin = at (0s) {
size => 1.0;
color => Color.GREEN;
rotation => 0.0;
}

var mid = at (0.5s) {
color => Color.PURPLE
tween Interpolator.EASEBOTH;
}

var end = at (1s) {
size => 3.0 tween Interpolator.LINEAR;
color => Color.RED
tween Interpolator.EASEBOTH;
rotation => 360.0
tween Interpolator.EASEBOTH;
}

keyFrames: [begin,mid,end]
}

onMouseClicked: function(e) {
myTimeline.start();
}
}
}
}

主こま動画は通常のオブジェクトから生成されるが、作業をより容易にするため、特有の構文、すなわち、=>、tween、およびat演算子が提供されることに留意せよ。この例では、主こまを「at」演算子を用いて生成した。これは、KeyFrameオブジェクトの直定数構築子である。



A.鍵語と予約語
この付録は、JavaFX言語において予約された単語を記録したものである。

表 A.1 鍵語と予約語
実装済み鍵語
abstract attribute bind break
class continue delete false for
function if import init insert
new not null package
private public return
super sizeof this throw
try true var while after
and as before catch
dur else exclusive extends
finally in bound
indexof into inverse lazy
on or replace step
with where instanceof override at
then tween

予約済み鍵語
assert by do first from
last let protected readonly typeof
lazy

0 件のコメント: