配列
💡 お知らせ: このドキュメントはAIによって翻訳されています。表現に違和感がある場合は、原文(英語)を参照するか、翻訳にご協力ください。
Flix は、ミュータブル(可変)な スコープ付き 配列(Array)をサポートしています。配列とは、同じ型を共有する固定長でミュータブルな要素の列です。配列はメモリ上に連続して配置されます。配列はミュータブルであるため、その要素は時間とともに変化し得ます。ただし、いったん作成すると、配列の長さは変更できません。
Flix では、配列の型は Array[t, r] です。ここで t は要素の型、r はそのリージョン(region)です。Flix のすべての可変メモリと同様に、すべての配列はいずれかのリージョンに属さなければなりません。配列の読み取りと書き込みは エフェクトを持つ 操作です。たとえば、型 Array[t, r] の配列から要素を読み取る操作はエフェクト r を持ちます。同様に、リージョン内で配列を作成する操作もエフェクトを持つ操作です。
配列は 常に アンボックス化(unboxed)されています。たとえば、型 Array[Int32, r] の配列は、プリミティブな32ビット整数の列として表現されます。すなわち JVM の用語で言えば、その配列は int[] として表現されます。Flix はプリミティブな整数を java.lang.Integer オブジェクトとしてボックス化することは決してありませんが、それでもジェネリックなコレクションや関数の中でプリミティブを使うことは許可しています。これは他の種類のプリミティブやプリミティブの配列についても同様です。
配列は低レベルのデータ構造であり、通常はより高レベルなデータ構造を実装するために使われます。したがって、そのようなデータ構造を実装する場合を除いては、配列の使用は控えめにすることをおすすめします。代わりに、MutList、MutDeque、MutSet、MutMap といったデータ構造を使うことをおすすめします。
ヒント: 要素の 伸長可能(growable) な可変列が必要な場合は、
MutListを使ってください。
配列リテラル
配列リテラルの構文は Array#{e1, e2, e3, ...} @ r の形をとります。ここで e1、e2 などは 要素式 であり、r は リージョン式 です。たとえば次のようになります。
region rc {
let fruits = Array#{"Apple", "Pear", "Mango"} @ rc;
println(Array.toString(fruits))
}
ここでは rc という名前のリージョンを導入しています。このリージョン内で、"Apple"、"Pear"、"Mango" という3つの文字列を含む配列 fruits を作成しています。fruits の型は Array[String, rc] です。リージョンの詳細については、リージョンの章を参照してください。
このプログラムを実行すると Array#{"Apple", "Pear", "Mango"} が出力されます。
配列の確保
Array.repeat 関数を使うと、同じ要素で満たされたサイズ n の配列を確保できます。たとえば次のようになります。
region rc {
let arr = Array.repeat(rc, 1_000, 42);
println(Array.toString(arr))
}
ここでは、各要素が値 42 を持つ、長さ 1_000 の配列 arr を作成しています。Array.repeat にはリージョン rc を引数として渡す必要がある点に注意してください。返される配列がどのリージョンに属するべきかを、この関数が知る必要があるからです。
ゼロから99までのすべての整数で満たされた配列を作成することもできます。
region rc {
let arr = Array.range(rc, 0, 100);
println(Array.toString(arr))
}
さらに、ほとんどのデータ構造は配列に変換できます。たとえば次のようになります。
region rc {
let fruitList = List#{"Apple", "Pear", "Mango"};
let fruitArray = List.toArray(rc, fruitList);
}
List.toArray にはリージョン rc を引数として渡す必要がある点に注意してください。返される配列がどのリージョンに属するべきかを、この関数が知る必要があるからです。
未初期化の要素を持つ配列の確保
Array.empty 関数を使うと、内容が未初期化の、指定した長さの配列を作成できます。たとえば次のようになります。
region rc {
let arr: Array[String, rc] = Array.empty(rc, 100);
// ... ここで `arr` を初期化します ...
}
ここでは、型 Array[String, rc] の長さ 100 の配列を作成しています。配列の期待される型を Flix に伝えるために、明示的な型注釈 : Array[String, rc] を使っています。
警告: 未初期化の要素を持つ配列を使うのは危険です。
未初期化の配列の要素は何になるのでしょうか? Flix は Java(および JVM)に従い、すべてのプリミティブ型および参照型に対して デフォルト値 を定義しています。たとえば Bool と Int32 のデフォルト値は、それぞれ false と 0 です。参照型のデフォルト値は null です。ですから注意してください! Flix には null 値はありませんが、適切に初期化されていない配列を読み取ることで間接的に null が持ち込まれてしまい、NullPointerException を引き起こす可能性があります。
配列の読み取りと書き込み
Array.get と Array.put を使うと、配列の特定の位置にある要素をそれぞれ取得・更新できます。たとえば次のようになります。
region rc {
let strings = Array.empty(rc, 2);
Array.put("Hello", 0, strings);
Array.put("World", 1, strings);
let s1 = Array.get(0, strings);
let s2 = Array.get(1, strings);
println("${s1} ${s2}")
}
ここでは長さ2の空の配列を作成しています。次に、位置ゼロに文字列 "Hello" を、位置1に文字列 "World" を格納します。続いて、その2つの文字列を取得して出力します。したがって、このプログラムをコンパイルして実行すると Hello World が出力されます。
!> パイプライン演算子を使うと、プログラムの一部をより 流れるようなスタイル(fluent-style) で書くこともできます。
let strings =
Array.empty(rc, 2) !>
Array.put("Hello", 0) !>
Array.put("World", 1);
配列のスライス
Array.slice を使って配列をスライスできます。配列のスライスとは、元の配列の部分範囲を新しく(浅く)コピーしたものです。たとえば次のようになります。
region rc {
let fruits = Array#{"Apple", "Pear", "Mango"} @ rc;
let result = Array.slice(rc, start = 1, end = 2, fruits);
println(Array.toString(result))
}
これは実行すると Array#{"Pear"} を出力します。
配列の長さの取得
Array.length 関数を使って配列の長さを計算できます。たとえば次のようになります。
region rc {
let fruits = Array#{"Apple", "Pear", "Mango"} @ rc;
println(Array.length(fruits))
}
これは実行すると 3 を出力します。
注意: 配列をインデックスベースで反復処理することはおすすめしません。代わりに、
Array.count、Array.forEach、Array.transformといった関数を使うことをおすすめします。
その他の配列操作
Array モジュールは、配列を扱うための関数を豊富に取りそろえています。いくつか挙げると、たとえば Array.append、Array.copyOfRange、Array.findLeft、Array.findRight、Array.sortWith、Array.sortBy などがあります。このモジュールは合計で100を超える関数をすぐに使える形で提供しています。