ついにベールを脱いだJavaFX

第3回基本的な文法を理解する

前回JavaFXでHello, World!アプリケーションを作成してみましたが、いかがだったでしょうか。宣言的文法によるGUIの構築の雰囲気だけでも感じていただけたと思います。

とはいうものの、Hello, World!アプリケーションはあまりにも単純です。もう少し複雑なアプリケーションを作るためには、JavaFX Scriptの文法を知らなくてはなりません。そこで、今回は基本的な文法について解説します。そして、次回は今回紹介した文法を駆使してアプリケーションを作ることに挑戦してみましょう。

データ型

JavaFX Scriptの基本となるデータ型は以下の通りです。

  • 文字列 String
  • 数値 Integer, Number
  • 論理値 Boolean
  • 時間間隔 Duration
  • Void
  • 関数 function

ここでは、関数型以外の型について説明します。関数型は、後述する関数の節で説明します。

文字列

JavaFX Scriptにおいて文字列を表す場合、シングルクォーテーション、もしくはダブルクォーテーションで文字列を囲みます。シングルクォーテーションとダブルクォーテーションの違いは特にありません。

文字列の型はStringで、基本的なデータ型として扱われます。

リスト1
var text1: String = 'Hello, World!';
var text2: String = "Hello, Japan!";

println(text1);
println(text2);

ここでは変数の定義に型名を指定していますが、式の右辺が文字列であるため、型名を省略して記述することもできます。このスクリプトを実行すると、以下のようになります。

Hello, World!
Hello, Japan!

もちろん、シングルクォーテーションとダブルクォーテーションを混ぜて使うことはできません。しかし、たとえばシングルクォーテーションで囲んでいる場合、ダブルクォーテーションを文字列中に使うことは可能です。

文字列を囲っている文字と同じ文字を文字列中に使うには¥でエスケープします。

リスト2
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"!

なお、インタープリタ版では複数の行で表される文字列を使用することができましたが、現在はサポートされていません。

文字列中に { } が存在すると、括弧内に埋めこまれた式が評価され、展開されます。

リスト3
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

なお、文字列中に { } を使用したい場合、¥でエスケープします。

リスト4
println("Hello, ¥{World¥}!");

これを実行すると次のようになります。

Hello, {World}!

なお、インタープリタ版では開括弧はエスケープが必要でしたが、閉括弧はエスケープが不要でした。1.0では、開括弧、閉括弧の両方ともエスケープが必要なので、ご注意ください。

文字列を連結するにはconcat関数を使用します。Javaのように演算子の + を使用することはできないので、ちょっと不便ですね。

リスト5
var greeting = "Hello, ";
var nation = "Japan";
var mark ="!";

var hello = greeting.concat(nation).concat(mark);

println(hello);

concat関数は文字列を返すので、この例のようにconcat関数を並べて記述することができます。実行してみると、次のようになります。

Hello, Japan!

また、式の埋めこみを使用して文字列を連結することも可能です。

リスト6
var text1 = "Supercalifragilistic";
var text2 = "espialidocious";

var text3 = "{text1}{text2}";

println(text3);

実行結果を以下に示します。

Supercalifragilisticespialidocious

数値

JavaFX Scriptでは、数値を2つの型で表します。整数型のIntegerと、浮動小数点数型のNumberです。Integerは、Javaのint、つまり32ビットの符号つき整数に対応します。同様にNumberはJavaのdouble、つまり符号部1ビット、指数部11ビット、仮数部52ビットで表される浮動小数点数に対応します。

リスト7
var num1: Integer = 1;
var num2: Number = 1.0;

var num3 = 5; // Integerが使用される
var num4 = 10.0; // Numberが使用される

インタープリタ版では、Integerがlongやjava.math.BigIntegerクラスで表される整数までサポートしていたのですが、現在はintで使用できる範囲に狭められてしまいました。

なお、IntegerからNumberへの代入は可能ですが、逆のNumberからIntegerへの代入はコンパイル時に精度が落ちている可能性があるという警告が発生します。これはJavaと同様です。NumberからIntegerに変換するにはintValue関数を使用します。

リスト8
var x: Integer;
var y: Number = 1.5;

x = y.intValue();

println("x: {x}");

これを実行してみましょう。

x: 1

結果からわかるように、小数点以下は切り捨てられます。

intValue関数は直接数値に対して記述することもできます。

リスト9
var x: Integer = 10.0.intValue();

var y: Integer = (2 + 5.5).intValue();

下の式で、2はInteger、5.5はNumberです。このようにIntegerとNumberの数値を演算すると、Numberに変換されます。これもJavaと同様です。

このようにintValue関数でNumberからIntegerに変換することができるのですが、ちょっと違和感があります。こういう場合、キャストを使用することもできます。JavaFX ScriptのキャストはJavaとは異なり、式の後に as 型名 という記述をします。

リスト10
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です。

使い方などを含めて、BooleanはJavaのbooleanと同じと考えることができます。ただし、否定演算子はJavaとは異なります。

リスト11
var x: Boolean = true;

println("not x: {not x}");

このコードのように、否定をする場合はnotを使用します。実行すると、trueの否定のfalseが出力されます。

not x: false

時間間隔

前回紹介したように、JavaFX Scriptでは時間間隔を表すDurationを使用することができます。

リスト12
var onemillis = 1ms;   // 1 ミリ秒
var onesec = 1s;       // 1 秒
var onemin = 1m;       // 1 分
var onehour = 1h;      // 1 時間

数値の後ろにスペースを加えずにms、s、m、hのいずれかを付加すると、Durationとして扱われます。それぞれ、ミリ秒、秒、分、時間を表します。日やナノ秒を直接表記することはできないので、他の時間単位を使用して表します。

リスト13
var onenanos = 0.001ms;   // 1 ナノ秒
var oneday = 24h;         // 1 日

この例のように、整数だけでなく小数点数でもDurationとして扱うことができます。

異なる時間単位を使用していても、比較を行うことは可能です。

リスト14

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は、Javaのvoidと同等です。Javaのvoidと同様に、関数が戻り値を返さない場合に使用します。後述する関数の節でVoidを使用した例を紹介します。

nullはデータ型ではなく値なのですが、ここでまとめて説明してしまいましょう。nullはJavaのnullと同様の使い方だけでなく、次のような使い方ができます。

リスト15
var x: Integer[] = [];

println("x == null : {x == null}");

var text = "";

println("text == null : {text == null}");

詳しくは後述しますが、[]は配列に相当するシーケンスを表しています。つまり、変数xはInteger型のシーケンスで、要素はありません。textは空の文字列です。

これらをnullと比較してみたらどうなるでしょう。実際にやってみました。

x == null : true
text == null : true

つまり、要素が空のシーケンスや空の文字列はいずれもnullと同等と見なされるということです。

変数と定数

今まで、変数は何度も使用してきましたが、正しい定義は説明していませんでした。変数は var キーワードを使用して定義します。

リスト16
var 変数名: 型;

変数の定義文で変数の初期化を行う場合、変数の型は明らかなので省略することが可能です。

リスト17
var message = Text {
    content: "Message"
};

この場合、式の右辺がTextオブジェクトを表しているので、自動的に変数messageの型はTextクラスとなります。

式の右辺のオブジェクトと異なるクラスを型としたい場合は明記する必要があります。

リスト18
import javafx.scene.Node;
import javafx.scene.text.Text;

var message: Node = Text {
    content: "Message"
};

ここでは、変数messageの型を、javafx.scene.text.Textクラスのスーパークラスであるjavafx.scene.Nodeクラスに指定しています。

次は定数です。定数はdefキーワードで定義します。

リスト19
def 変数名: 型 = 値;

定数なので、必ず定義する時に初期化も一緒に行う必要があります。もちろん、defで定義した定数を後から変更することはできません。

リスト20
def pi = 3.1415;
def hello = "Hello, World!";

関数

JavaFX Scriptではメソッドを関数として表します。関数の定義はfunctionキーワードを使用して行います。なお、インタープリタ版ではfunctionだけでなく、operationも使用されていましたが、functionに統一されました。

リスト21
function 関数名(引数1: 型, 引数2: 型): 戻り値の型 {
    // 関数で行う処理 
}

ここでは2つの引数を取る場合を示しましたが、もちろん引数がない場合や、もっと多数の引数でもかまいません。ただし、可変引数は使用できません。

戻り値はJavaと同じくreturn文で記述します。return文がない場合、関数の最後の行が示しているオブジェクトを戻り値とします。

リスト22
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が表記されていますが、subはreturn文がありません。しかし、関数の最後の行、ここでは1行しかないのでx - yが戻り値となります。

実行すると、次のようになります。

10.0 + 5.0 = 15.0
10.0 - 5.0 = 5.0

戻り値がない場合、戻り値の型はVoidとして定義します。

リスト23
function hello(name: String): Void {
    println("Hello, {name}!");
};

また、戻り値の型の定義がないメソッドはreturn文、もしくは関数の最後の行が示すオブジェクトの型になります。したがって、次の例のhelloメソッドの戻り値の型はStringとなります。

リスト24
function hello(name: String) {
    "Hello, {name}!";
}

println(hello("World");

したがって、このスクリプトを実行すると次のように出力されます。

Hello, World!

名前のない無名関数を定義することも可能です。

JavaFX Scriptでは変数やアトリビュートを関数型とすることができます。関数型の変数/アトリビュートに関数を代入する時に無名関数が使用されます。無名関数は関数名を表記する部分にfunctionと記述します。

リスト25
// 変数を関数型で定義し、後から無名関数を代入する
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)}");

変数の定義に関数型を明示する場合は、引数と戻り値を明記します。変数に無名関数を代入する場合、定義した引数および戻り値と合わせるようにします。関数型の変数が示している関数をコールする場合、通常の関数をコールするのと変わりません。ちなみに、インタープリタ版では(add)(a, b)のように変数名を括弧でくくる必要があったのですが、1.0では括弧が不必要となりました。

この関数型の変数はイベント処理などのハンドラなどに多用されます。Javaではリスナをインプリメントした無名クラスを作成してメソッドを実装しますが、JavaFX Scriptでは直接関数を代入できるため簡潔に表すことができます。

オブジェクトの生成

前回、Hello, World!サンプルの中でいくつかオブジェクトを作成しました。ここで、もう1度オブジェクトの生成について整理しておきましょう。

オブジェクトを生成する前に、使用するクラスのimport文を記述する必要があります。これはJavaと同様です。オブジェクトを生成するには、次のように記述します。

リスト26
クラス名 { }

ただし、Javaのクラスのオブジェクトを生成するには new を使用します。

JavaFX Scriptはコンストラクタという概念はありません。その代わり、並括弧の中でアトリビュートの初期化を行います。たとえば、Colorクラスにはred, green, blue, opacityの4つのアトリビュートがあります。すべて値は0から1までのNumberです。したがって、Colorオブジェクトを生成するには次のように記述します。

リスト27
Color {
    red: 1.0
    green: 0.5
    blue: 0.5
    opacity: 0.8
}

アトリビュートの初期化は アトリビュート名: 値 のように記述します。文末はセミコロン、カンマ, 何もなしのいずれもOKです。また、アトリビュートの初期化する順序は決まっていません。したがって、先ほどのColorオブジェクトは次のようにも記述できます。

リスト28
Color {
    opacity: 0.8;
    green: 0.5,
    red: 1.0
    blue: 0.5;
}

複数のアトリビュートの初期化を1行に書くことも可能です。1行に書くとしても、文末はセミコロン、カンマ、無しのどれでもかまいません。

リスト29
Color {
    opacity: 0.8; green: 0.5, red:
    1.0 blue: 0.5;
}

アトリビュートの初期化のために、何らかの処理が必要なこともあるかもしれません。そのような場合、次のように記述することができます。

リスト30
クラス名 {
    アトリビュート名: {
        // 何らかの処理
        
        アトリビュートに代入したいオブジェクト
    }
}

並括弧の中の最後の行にアトリビュートに代入したいオブジェクトを記述します。returnは書きませんが、関数の戻り値のようなものと考えることができます。たとえば、次のような使い方をします。

リスト31
var n = 100;

Foo {
    sum: {
        var tmpSum = 0;
        for (x in [0..n]) {
            tmpSum += x;
        }
        tmpSum;
    }
}

このように記述することで、Fooオブジェクトのsumアトリビュートには0からn(=100)までの和が代入されます。

ここでは、オブジェクトの生成について紹介しましたが、クラスを作ることについてはもう少しJavaFX Scriptに慣れてからにしましょう。

シーケンス

シーケンスというのは、Javaの配列に相当する概念です。しかし、配列にはない機能も持っています。

シーケンスの定義

シーケンスは[ ]で囲い、要素をカンマ区切りで記述します。

リスト32
var seq1: Integer[];
seq1 = [0, 1, 2, 3, 4, 5];

var seq2: String[];
seq2 = ["spade", "club", "heart", "diamond"];

もちろん、定義と初期化を一緒に行うのであれば、型名は省略できます。

リスト33
var seq3 = [0, 1, 2, 3, 4, 5];

var seq4 = ["spade", "club", "heart", "diamond"];

JavaFXでは多次元のシーケンスを扱うことができません。多重配列のようにシーケンスの中にシーケンスを表記すると、展開されて1次元のシーケンスとなります。たとえば、次のコードはどのように評価されるでしょうか。

リスト34
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

どちらも展開されてしまうので、同一のシーケンスと見なすことができます。

ここで、seq5.toString()のようにtoString関数を使用したのは、toString関数を使用しないと要素が並べて表示されてしまうためです。たとえば、次のコードを考えてみます。

リスト35
var seq = ["spade", "club", "heart", "diamond"];
println("{seq}");

これを実行すると、以下のようになります。

spadeclubheartdiamond

これではちょっとわかりにくいので、toString関数を使用したわけです。

では、本題に戻りましょう。シーケンスとして定義した変数もしくはアトリビュートに、シーケンスでない要素を代入すると、指定した要素だけを持つシーケンスとして解釈されます。

リスト36
var seq: Integer[];
 
// シーケンスにシーケンスでないものが代入された場合
// 指定した要素のみを持つシーケンスとして解釈される
seq = 10;

println("seq: {seq.toString()}");

ここでは、変数seqに10を代入しています。これはどのように解釈されるでしょう。実行結果を次に示します。

seq: [ 10 ]

このように10だけを要素に持つシーケンスと解釈されました。

この性質は、すでに前回の記事の中で使用しています。前回使用したhello.Mainクラスを以下に示します。

リスト37
Stage {
    title: "Hello"
    width: 250
    height: 80
    scene: Scene {
        content: Text {
            font : Font {
                size : 24
            }
            x: 10, y: 30
            content: "Hello, World!"
        }
    }
}

赤字で示したSceneクラスのcontentアトリビュートは、実際にはTextクラスのスーパークラスであるNodeクラスのシーケンスとして定義されています。しかし、ここでは単一のTextオブジェクトのみを代入しています。それが、ここで示した文法のおかげで、シーケンスを示す [ ] を記述しなくても正しく動作していたのです。

シーケンスのその他の定義法

数値を要素に取るシーケンスでは次のような定義をすることもできます。

  1. 始値と終値で指定した範囲
  2. 始値と終値で指定した範囲 ただし終値は含まない
  3. 始値と終値およびステップを指定した範囲

それぞれの次のように表記します。

リスト38
// 範囲の指定
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 ]

ステップを負の値にする場合、始値が終値より大きくなるようにしてください。

また、for文を使用してシーケンスの定義を行うこともできます。詳しくは次々回に説明しますが、JavaFX Scriptのfor文は、Java SEの拡張for文と同じような使い方をします。このfor文を以下のように記述すると、シーケンスを作成することができるのです。

リスト39

var nums = [0, 1, 2, 3, 4];

var odds = for (value in nums) {
    value * 2;
};

println(odds);

このコードは、基となるシーケンスnumsをまず用意しておきます。そして、for文が解釈されると、numsの個々の要素を2倍したシーケンスが作成されます。上のコードを実行すると次のようになります。

[ 0, 2, 4, 6, 8 ]

シーケンスの部分からシーケンスを作成する

シーケンスの部分を取りだして、新しいシーケンスを作ることも可能です。これを行うには次のように表記します。

リスト40
配列名[ 変数 | 論理式 ]

論理式がtrueになる要素が抽出されて、新しいシーケンスになります。いくつか例を示しましょう。

リスト41
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で割って、余りがない要素(つまり偶数です)だけを抽出したシーケンスが作成されます。b) は要素の中から、5より小さい要素を抽出します。c) の例は同様に6以上の要素を抽出します。最後のd) の例はnodesというNodeクラスの派生クラスのオブジェクトによるシーケンスをまず用意します。実際にはそれぞれのクラスのimport文が必要ですが、ここでは省略せていただきました。

これらのNodeオブジェクトの内、Circleオブジェクトだけを抽出します。オブジェクトがあるクラスのインスタンスであるかどうかをチェックするには、Javaと同じくinstanceofを使用します。では、それぞれ実行するとどうなるでしょうか?

偶数: [ 0, 2, 4, 6, 8 ]
5未満: [ 0, 1, 2, 3, 4 ]
6以上: [ 6, 7, 8, 9 ]
Circle: [ Circle [ <<省略>> ] ]

Circleオブジェクトを出力すると、すべてのアトリビュートを出力してしまうため、ここでは省略させていただきました。

また、インデックスによる抽出も可能です。インデックスの指定は、数値のシーケンス作成で使用したものと似ています。しかし、数値のシーケンス作成で指定したものは実際の要素になりますが、ここで使用するのはインデックスです。

リスト42
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..bは、a番目の要素からb番目の要素を抽出するということです。a..<bだと、a番目の要素からb番目の要素の前までという意味になります。終わりのインデックスを指定していない場合は、最後までという意味になります。<が入ると、最後の要素は含まないという意味になります。

では、実際に実行してみましょう。

people2: [ Quartet, Quintet, Sextet ]
people3: [ Trio, Quartet, Quintet, Sextet ]
people4: [ Quartet, Quintet, Sextet, Seventet ]
people5: [ Trio, Quartet, Quintet, Sextet ]

要素へのアクセス

シーケンスの各要素へのアクセスは、配列と同じように括弧内にインデックスを指定して行います。インデックスは0から始まります。

リスト43
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

なお、シーケンスの長さはsizeof演算子を使用して求めます。sizeof演算子はシーケンス名の前に記述します。

リスト44
var suits = ["spade", "club", "heart", "diamond"];

println("Size of suites: {sizeof suits}");

実行すると予想通り4が出力されます。

Size of suites: 4

要素の挿入、更新、削除

配列とは異なり、シーケンスでは任意の位置に要素を追加することができます。同様に任意の位置の要素を更新することや削除することも可能です。

まず、要素の挿入を説明しましょう。要素を挿入するにはinsertを使用します。

リスト45
// シーケンスの最後に挿入
insert 挿入する要素 into シーケンス

// インデックスで指定した要素の前に挿入
insert 挿入する要素 before シーケンス[インデックス]

// インデックスで指定した要素の後に挿入
insert 挿入する要素 after シーケンス[インデックス]

これらについて、実際の例を示しました。

リスト46
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の最後に挿入なので、["Young", "Lennon", "Dylan", "Simon", "Mitchell"]になるはずです。

そして、次はartists[1]の前に挿入です。artists[1]は"Lennon"ですから、["Young", "King", "Lennon", "Dylan", "Simon", "Mitchell"]になります。

最後の挿入はartists[3]の後に挿入します。artists[3]は"Dylan"なので、最終的にartistsは["Young", "King", "Lennon", "Dylan", "Taylor", "Simon", "Mitchell"]になるはずです。

では、実際に試してみましょう。

Mitchellを挿入: [ Young, Lennon, Dylan, Simon, Mitchell ]
King を挿入: [ Young, King, Lennon, Dylan, Simon, Mitchell ]
Taylor を挿入: [ Young, King, Lennon, Dylan, Taylor, Simon, Mitchell ]

予想通りのシーケンスになりました。

要素の更新は、Javaの配列と同じです。

リスト47
var artists = ["Young", "Lennon", "Dylan", "Simon"];

artists[3] = "Garfunkel";
println("SimonをGarfunkelに更新: {artists.toString()}");

この例では、3番目の要素をSimonからGarfunkelに更新しています。

SimonをGarfunkelに更新: [ Young, Lennon, Dylan, Garfunkel ]

次は削除です。削除にはdeleteを使用します。削除する要素の指定には、要素を直接指定する方法と、インデックスで指定する方法があります。

リスト48
delete 削除する要素 from シーケンス

delete シーケンス[削除するインデックス]

要素を指定する場合は、削除する要素の後にfromを付加してシーケンス名を記述します。インデックスを指定する場合は、削除するインデックスを明記します。これも実際に試してみましょう。

リスト49

var artists = ["Young", "Lennon", "Dylan", "Simon"];

delete "Lennon" from artists;
println("Lennon を削除: {artists.toString()}");

delete artists[2];
println("2番目の要素を削除: {artists.toString()}");

はじめに"Lennon"を削除するので、シーケンスは["Young", "Dylan", "Simon"]になるはずです。そして、2番目の要素を削除するので、"Simon"が削除されます。実行結果は次のようになりました。

Lennon を削除: [ Young, Dylan, Simon ]
2番目の要素を削除: [ Young, Dylan ]

シーケンスの比較

シーケンスが等しいかどうかは、シーケンスの長さが同じ、かつ要素が同じということになります。

リスト50
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と定義が異なるシーケンスでも、要素が同一であれば等しいと見なされることがわかります。

シーケンスに対する演算

シーケンスに適用できる演算子には、reverse演算子とindexof演算子があります。reverse演算子はシーケンスの要素の順序を反対にする演算を行います 。

リスト51
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を行っても、基となるシーケンスは変化しないので注意してください。つまり、この例ではnumsは0から5の順序のままです。

indexof演算子は、for文と共に用い、ループ中のインデックスを示します 。

リスト52
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

さて、今回は基本的な文法を紹介しました。これ以外にも制御文などがありますが、今回は触れませんでした。それらについては次々回に解説する予定です。次回は今回紹介した文法を使用して、Hello, World!からの脱却を狙いましょう。

おすすめ記事

記事・ニュース一覧