前回、
とはいうものの、
データ型
JavaFX Scriptの基本となるデータ型は以下の通りです。
- 文字列 String
- 数値 Integer, Number
- 論理値 Boolean
- 時間間隔 Duration
- Void
- 関数 function
ここでは、
文字列
JavaFX Scriptにおいて文字列を表す場合、
文字列の型はStringで、
var text1: String = 'Hello, World!';
var text2: String = "Hello, Japan!";
println(text1);
println(text2);
ここでは変数の定義に型名を指定していますが、
Hello, World! Hello, Japan!
もちろん、
文字列を囲っている文字と同じ文字を文字列中に使うには¥でエスケープします。
var text3 = 'Hello, "World"!';
var text4 = "Hello, 'Japan'!";
println(text3);
println(text4);
var text5 = 'Hello, ¥'Asia¥'!';
var text6 = "Hello, ¥"Tokyo¥"!";
println(text5);
println(text6);
実行結果は次のようになります。
Hello, "World"! Hello, 'Japan'! Hello, 'Asia'! Hello, "Tokyo"!
なお、
文字列中に { } が存在すると、
var nation = "Japan";
var text1 = 'Hello, {nation}!';
println(text1);
var x = 5;
var y = 10;
var text2 = "{x} + {y} = {x + y}";
println(text2);
この例のように式の埋めこみを使用すると、
Hello, Japan! 5 + 10 = 15
なお、
println("Hello, ¥{World¥}!");
これを実行すると次のようになります。
Hello, {World}!
なお、
文字列を連結するにはconcat関数を使用します。Javaのように演算子の + を使用することはできないので、
var greeting = "Hello, ";
var nation = "Japan";
var mark ="!";
var hello = greeting.concat(nation).concat(mark);
println(hello);
concat関数は文字列を返すので、
Hello, Japan!
また、
var text1 = "Supercalifragilistic";
var text2 = "espialidocious";
var text3 = "{text1}{text2}";
println(text3);
実行結果を以下に示します。
Supercalifragilisticespialidocious
数値
JavaFX Scriptでは、
var num1: Integer = 1;
var num2: Number = 1.0;
var num3 = 5; // Integerが使用される
var num4 = 10.0; // Numberが使用される
インタープリタ版では、
なお、
var x: Integer;
var y: Number = 1.5;
x = y.intValue();
println("x: {x}");
これを実行してみましょう。
x: 1
結果からわかるように、
intValue関数は直接数値に対して記述することもできます。
var x: Integer = 10.0.intValue();
var y: Integer = (2 + 5.5).intValue();
下の式で、
このようにintValue関数でNumberからIntegerに変換することができるのですが、
var x: Integer = 10.0 as Integer;
var y: Integer = (2 + 5.5) as Integer;
var a: Number = 20.3;
var z: Integer = a as Integer;
println("x: {x} y: {y} z: {z}");
実行結果を次に示します。
x: 10 y: 7 z: 20
論理値
論理値はBooleanで表されます。取りうる値はtrueとfalseです。
使い方などを含めて、
var x: Boolean = true;
println("not x: {not x}");
このコードのように、
not x: false
時間間隔
前回紹介したように、
var onemillis = 1ms; // 1 ミリ秒
var onesec = 1s; // 1 秒
var onemin = 1m; // 1 分
var onehour = 1h; // 1 時間
数値の後ろにスペースを加えずにms、
var onenanos = 0.001ms; // 1 ナノ秒
var oneday = 24h; // 1 日
この例のように、
異なる時間単位を使用していても、
var onemillis = 1ms; // 1 ミリ秒
var onesec = 1s; // 1 秒
var onemin = 1m; // 1 分
var onehour = 1h; // 1 時間
println("{onesec}/1000 == {onemillis} : {onesec/1000 == onemillis}");
println("{onehour} == {onemin} * 60 : {onehour == onemin*60}");
このスクリプトを実行してみると以下のようになりました。
1000ms/1000 == 1ms : true 3600000ms == 60000ms * 60 : true
この結果からすると、
時間間隔については、
Voidとnull
Voidは、
nullはデータ型ではなく値なのですが、
var x: Integer[] = [];
println("x == null : {x == null}");
var text = "";
println("text == null : {text == null}");
詳しくは後述しますが、
これらをnullと比較してみたらどうなるでしょう。実際にやってみました。
x == null : true text == null : true
つまり、
変数と定数
今まで、
var 変数名: 型;
変数の定義文で変数の初期化を行う場合、
var message = Text {
content: "Message"
};
この場合、
式の右辺のオブジェクトと異なるクラスを型としたい場合は明記する必要があります。
import javafx.scene.Node;
import javafx.scene.text.Text;
var message: Node = Text {
content: "Message"
};
ここでは、
次は定数です。定数はdefキーワードで定義します。
def 変数名: 型 = 値;
定数なので、
def pi = 3.1415;
def hello = "Hello, World!";
関数
JavaFX Scriptではメソッドを関数として表します。関数の定義はfunctionキーワードを使用して行います。なお、
function 関数名(引数1: 型, 引数2: 型): 戻り値の型 {
// 関数で行う処理
}
ここでは2つの引数を取る場合を示しましたが、
戻り値はJavaと同じくreturn文で記述します。return文がない場合、
function add(x: Number, y: Number): Number {
return x + y;
}
function sub(x: Number, y: Number): Number {
// 下の行が戻り値として使用される
x - y;
}
var a = 10.0;
var b = 5.0;
println("{a} + {b} = {add(a, b)}");
println("{a} - {b} = {sub(a, b)}");
add関数はreturnが表記されていますが、
実行すると、
10.0 + 5.0 = 15.0 10.0 - 5.0 = 5.0
戻り値がない場合、
function hello(name: String): Void {
println("Hello, {name}!");
};
また、
function hello(name: String) {
"Hello, {name}!";
}
println(hello("World");
したがって、
Hello, World!
名前のない無名関数を定義することも可能です。
JavaFX Scriptでは変数やアトリビュートを関数型とすることができます。関数型の変数/アトリビュートに関数を代入する時に無名関数が使用されます。無名関数は関数名を表記する部分にfunctionと記述します。
// 変数を関数型で定義し、後から無名関数を代入する
var add: function(Number, Number): Number;
add = function(x: Number, y: Number): Number {
return x + y;
}
// 変数の定義時に、無名関数を代入する
var sub = function(x: Number, y: Number): Number {
x - y;
}
var a = 10.0;
var b = 5.0;
// 関数型の変数は、通常の関数と同様にコール可
println("{a} + {b} = {add(a, b)}");
println("{a} - {b} = {sub(a, b)}");
変数の定義に関数型を明示する場合は、
この関数型の変数はイベント処理などのハンドラなどに多用されます。Javaではリスナをインプリメントした無名クラスを作成してメソッドを実装しますが、
オブジェクトの生成
前回、
オブジェクトを生成する前に、
クラス名 { }
ただし、
JavaFX Scriptはコンストラクタという概念はありません。その代わり、
Color {
red: 1.0
green: 0.5
blue: 0.5
opacity: 0.8
}
アトリビュートの初期化は アトリビュート名: 値 のように記述します。文末はセミコロン、
Color {
opacity: 0.8;
green: 0.5,
red: 1.0
blue: 0.5;
}
複数のアトリビュートの初期化を1行に書くことも可能です。1行に書くとしても、
Color {
opacity: 0.8; green: 0.5, red:
1.0 blue: 0.5;
}
アトリビュートの初期化のために、
クラス名 {
アトリビュート名: {
// 何らかの処理
アトリビュートに代入したいオブジェクト
}
}
並括弧の中の最後の行にアトリビュートに代入したいオブジェクトを記述します。returnは書きませんが、
var n = 100;
Foo {
sum: {
var tmpSum = 0;
for (x in [0..n]) {
tmpSum += x;
}
tmpSum;
}
}
このように記述することで、
ここでは、
シーケンス
シーケンスというのは、
シーケンスの定義
シーケンスは[ ]で囲い、
var seq1: Integer[];
seq1 = [0, 1, 2, 3, 4, 5];
var seq2: String[];
seq2 = ["spade", "club", "heart", "diamond"];
もちろん、
var seq3 = [0, 1, 2, 3, 4, 5];
var seq4 = ["spade", "club", "heart", "diamond"];
JavaFXでは多次元のシーケンスを扱うことができません。多重配列のようにシーケンスの中にシーケンスを表記すると、
var seq5 = [["spade", "club"], ["heart", "diamond"]];
var seq6 = ["spade", ["club", "heart"], "diamond"];
print("{seq5.toString()} == {seq6.toString()}: {seq5 == seq6}");
では、
[ spade, club, heart, diamond ] == [ spade, club, heart, diamond ]: true
どちらも展開されてしまうので、
ここで、
var seq = ["spade", "club", "heart", "diamond"];
println("{seq}");
これを実行すると、
spadeclubheartdiamond
これではちょっとわかりにくいので、
では、
var seq: Integer[];
// シーケンスにシーケンスでないものが代入された場合
// 指定した要素のみを持つシーケンスとして解釈される
seq = 10;
println("seq: {seq.toString()}");
ここでは、
seq: [ 10 ]
このように10だけを要素に持つシーケンスと解釈されました。
この性質は、
Stage {
title: "Hello"
width: 250
height: 80
scene: Scene {
content: Text {
font : Font {
size : 24
}
x: 10, y: 30
content: "Hello, World!"
}
}
}
赤字で示したSceneクラスのcontentアトリビュートは、
シーケンスのその他の定義法
数値を要素に取るシーケンスでは次のような定義をすることもできます。
- 始値と終値で指定した範囲
- 始値と終値で指定した範囲 ただし終値は含まない
- 始値と終値およびステップを指定した範囲
それぞれの次のように表記します。
// 範囲の指定
var seq1 = [1..10];
// 範囲の指定 (終値を含まない)
var seq2 = [1..<10];
// ステップを明記した範囲の指定
var seq3 = [1..10 step 2];
// ステップには負の値も可
var seq4 = [10..0 step -2];
println("seq1: {seq1.toString()}");
println("seq2: {seq2.toString()}");
println("seq3: {seq3.toString()}");
println("seq4: {seq4.toString()}");
このスクリプトを実行すると次のようになりました。
seq1: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] seq2: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] seq3: [ 1, 3, 5, 7, 9 ] seq4: [ 10, 8, 6, 4, 2, 0 ]
ステップを負の値にする場合、
また、
var nums = [0, 1, 2, 3, 4];
var odds = for (value in nums) {
value * 2;
};
println(odds);
このコードは、
[ 0, 2, 4, 6, 8 ]
シーケンスの部分からシーケンスを作成する
シーケンスの部分を取りだして、
配列名[ 変数 | 論理式 ]
論理式がtrueになる要素が抽出されて、
var nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// a) 偶数のシーケンス
var nums1 = nums[n | n mod 2 == 0];
println("偶数: {nums1.toString()}");
// b) 5 未満のシーケンス
var nums2 = nums[n | n < 5];
println("5未満: {nums2.toString()}");
// c) 6 以上のシーケンス
var nums3 = nums[n | n >= 6];
println("6以上: {nums3.toString()}");
var nodes:Node[] = [Rectangle{}, Circle{}, Text{}];
// d) Circleオブジェクトのみ
var circles = nodes[n | n instanceof Circle];
println("Circle: {circles.toString()}");
はじめの0から9までを要素にとるシーケンスを作成しておきます。a) の例は各要素を2で割って、
これらのNodeオブジェクトの内、
偶数: [ 0, 2, 4, 6, 8 ] 5未満: [ 0, 1, 2, 3, 4 ] 6以上: [ 6, 7, 8, 9 ] Circle: [ Circle [ <<省略>> ] ]
Circleオブジェクトを出力すると、
また、
var people = ["Solo", "Duo", "Trio", "Quartet", "Quintet", "Sextet", "Seventet"];
// 第3要素から第5要素
var people2 = people[3..5];
println("people2: {people2.toString()}");
// 第3要素から第6要素の前
var people3 = people[2..<6];
println("people3: {people3.toString()}");
// 第3要素以降
var people4 = people[3..];
println("people4: {people4.toString()}");
// 第2要素以降、最終要素を含まず
var people5 = people[2..<];
println("people5: {people5.toString()}");
a..
では、
people2: [ Quartet, Quintet, Sextet ] people3: [ Trio, Quartet, Quintet, Sextet ] people4: [ Quartet, Quintet, Sextet, Seventet ] people5: [ Trio, Quartet, Quintet, Sextet ]
要素へのアクセス
シーケンスの各要素へのアクセスは、
var suits = ["spade", "club", "heart", "diamond"];
println("suites[0]: {suits[0]}");
println("suites[1]: {suits[1]}");
println("suites[2]: {suits[2]}");
println("suites[3]: {suits[3]}");
実行結果を次に示します。
suites[0]: spade suites[1]: club suites[2]: heart suites[3]: diamond
なお、
var suits = ["spade", "club", "heart", "diamond"];
println("Size of suites: {sizeof suits}");
実行すると予想通り4が出力されます。
Size of suites: 4
要素の挿入、更新、削除
配列とは異なり、
まず、
// シーケンスの最後に挿入
insert 挿入する要素 into シーケンス
// インデックスで指定した要素の前に挿入
insert 挿入する要素 before シーケンス[インデックス]
// インデックスで指定した要素の後に挿入
insert 挿入する要素 after シーケンス[インデックス]
これらについて、
var artists = ["Young", "Lennon", "Dylan", "Simon"];
insert "Mitchell" into artists;
println("Mitchellを挿入: {artists.toString()}");
insert "King" before artists[1];
println("King を挿入: {artists.toString()}");
insert "Taylor" after artists[3];
println("Taylor を挿入: {artists.toString()}");
はじめの挿入はartistsの最後に挿入なので、
そして、
最後の挿入はartists[3]の後に挿入します。artists[3]は"Dylan"なので、
では、
Mitchellを挿入: [ Young, Lennon, Dylan, Simon, Mitchell ] King を挿入: [ Young, King, Lennon, Dylan, Simon, Mitchell ] Taylor を挿入: [ Young, King, Lennon, Dylan, Taylor, Simon, Mitchell ]
予想通りのシーケンスになりました。
要素の更新は、
var artists = ["Young", "Lennon", "Dylan", "Simon"];
artists[3] = "Garfunkel";
println("SimonをGarfunkelに更新: {artists.toString()}");
この例では、
SimonをGarfunkelに更新: [ Young, Lennon, Dylan, Garfunkel ]
次は削除です。削除にはdeleteを使用します。削除する要素の指定には、
delete 削除する要素 from シーケンス
delete シーケンス[削除するインデックス]
要素を指定する場合は、
var artists = ["Young", "Lennon", "Dylan", "Simon"];
delete "Lennon" from artists;
println("Lennon を削除: {artists.toString()}");
delete artists[2];
println("2番目の要素を削除: {artists.toString()}");
はじめに"Lennon"を削除するので、
Lennon を削除: [ Young, Dylan, Simon ] 2番目の要素を削除: [ Young, Dylan ]
シーケンスの比較
シーケンスが等しいかどうかは、
var seq1 = [0, 1, 2, 3, 4, 5];
var seq2 = [0, 1, 2, 3, 4, 5, 6];
var seq3 = [0, 1, 2, 3, 4];
var seq4 = [5, 4, 3, 2, 1, 0];
var seq5 = [];
var seq6 = [0, 1, 2, 3, 4, 5];
var seq7 = [0..5];
var seq8 = [0..<6];
println("seq1 == seq2: {seq1 == seq2}");
println("seq1 == seq3: {seq1 == seq3}");
println("seq1 == seq4: {seq1 == seq4}");
println("seq1 == seq5: {seq1 == seq5}");
println("seq1 == seq6: {seq1 == seq6}");
println("seq1 == seq7: {seq1 == seq7}");
println("seq1 == seq8: {seq1 == seq8}");
seq1とseq6が等しいということはわかりますが、
seq1 == seq2: false seq1 == seq3: false seq1 == seq4: false seq1 == seq5: false seq1 == seq6: true seq1 == seq7: true seq1 == seq8: true
seq7やseq8のようにseq1と定義が異なるシーケンスでも、
シーケンスに対する演算
シーケンスに適用できる演算子には、
var nums = [0..5];
var nums2 = reverse nums;
println("reverse nums: {nums2.toString()}");
0から5までの整数のシーケンスをreverseで変換すると、
reverse nums: [ 5, 4, 3, 2, 1, 0 ]
reverseを行っても、
indexof演算子は、
var nums = ["Zero", "One", "Two", "Three", "Four"];
for (num in nums) {
println("nums[{indexof num}]: {num}");
}
このスクリプトを実行した結果を次に示します。
nums[0]: Zero nums[1]: One nums[2]: Two nums[3]: Three nums[4]: Four
さて、