配列について解説します。
可変長配列
ArrayList
については、9章 containerに説明を記載します。
Zenの配列型では、要素数は型の一部であり、その要素数はコンパイル時に計算可能である必要があります。
examples/ch02-primitives/src/arrays.zen:4:10
test "array basics" {
const array = [4]i32{ 1, 2, 3, 4 };
ok(@TypeOf(array) == [4]i32);
ok(array.len == 4);
ok(array[0] == 1);
ok(array[3] == 4);
}
上の例では、変数array
の型は、[4]i32
です。配列リテラル[4]i32{ 1, 2, 3, 4 }
により、変数を初期化しています。変数の型は、配列リテラルから型推論が可能なため、変数array
の型は省略しています。
.len
で配列の長さを取得することができ、[インデックス]
で個別の要素にインデックスアクセスすることが可能です。インデックスの型はusize
である必要があります。
次のコードはコンパイルエラーになります。
test "array index" {
const array = [4]i32{ 1, 2, 3, 4 };
var index: i32 = 0;
ok(array[index] == 1);
}
error[E02046]: expected 'usize', found 'i32'
ok(array[index] == 1);
~
このような場合、@intCast
を利用して、index
をusize
に変換する必要があります。
examples/ch02-primitives/src/arrays.zen:12:16
test "array index" {
const array = [_]i32{ 1, 2, 3, 4 };
var index: i32 = 0;
ok(array[@intCast(usize, index)] == 1);
}
ノート: ただし、インデックスの値がコンパイル時計算可能であれば、暗黙の型変換が行われるため、
@intCast
による明示的な変換は不要です。
繰り返しになりますが、要素数が型の一部であることに注意して下さい。要素数4
の配列と要素数5
の配列は、違う型になります。
examples/ch02-primitives/src/arrays.zen:18:23
test "array types" {
const array1 = [4]i32{ 1, 2, 3, 4 };
const array2 = [5]i32{ 1, 2, 3, 4, 5 };
ok(@TypeOf(array1) != @TypeOf(array2));
}
配列をvar
で定義すると、各要素の値を更新することができます。
examples/ch02-primitives/src/arrays.zen:25:29
test "var array" {
var array = [4]i32{ 1, 2, 3, 4 };
array[0] = 5;
ok(array[0] == 5);
}
undefined
を代入することで、配列の値を不定値で初期化することができます。
examples/ch02-primitives/src/arrays.zen:31:34
test "array containing undefined values" {
var array: [100]i32 = undefined;
ok(array.len == 100);
}
配列リテラルとは、配列型に対してプログラム内に直接記述した値を意味します。構文は[要素数]要素型{ 値, ... }
です。
配列要素数を明示的に記述する方法と、配列リテラルの{ 値, ... }
から要素数を推論させる記述方法があります。要素数を推論させる場合は、[_]要素型{ 値, ... }
のように、要素数をアンダースコア (_
) にします。
examples/ch02-primitives/src/arrays.zen:36:41
test "array literals" {
const array1 = [4]i32{ 1, 2, 3, 4 };
const array2 = [_]i32{ 1, 2, 3, 4 };
// `array1`と`array2`は同じ型
ok(@TypeOf(array1) == @TypeOf(array2));
}
配列の全要素を順番に取得したい場合、for
を利用します。for (配列) | キャプチャ変数名 |
という構文を利用します。for
の包括的な利用方法については、4章 繰り返しを参照して下さい。
examples/ch02-primitives/src/arrays.zen:43:51
test "iterate over an array" {
var array = [_]i32{ 1, 2, 3, 4 };
var sum: i32 = 0;
// 各要素は`elem`として取得可能
for (array) |elem| {
sum += elem;
}
ok(sum == 10);
}
array
自体がvar
なのにも関わらず、上記の例ではelem
はconst
な値になります。そのため、elem
の値を変更することはできません。
次のコードはコンパイルエラーになります。
for (array) |elem| {
elem = 0;
}
error[E02030]: cannot assign to constant variable
elem = 0;
~
配列の要素自体を更新したい場合、各要素の参照を取得し (*
をつけて配列要素をキャプチャし)、参照外し (デリファレンス) 演算子 (.*
) を使用します。
examples/ch02-primitives/src/arrays.zen:53:60
test "update an array in for loop" {
var array = [_]i32{ 1, 2, 3, 4 };
// 各要素は`elem.*`として読み書き可能
for (array) |*elem| {
elem.* = elem.* * elem.*;
}
ok(array[3] == 16);
}
for
ブロック内で取得している要素のインデックスを使用したい場合、for (配列) | キャプチャ変数名, インデックス |
という構文を利用します。
examples/ch02-primitives/src/arrays.zen:62:72
test "get index of an array in for loop" {
var array = [_]usize{ 1, 2, 3, 4 };
// `i`に`usize`型のインデックスが格納される
for (array) |*elem, i| {
elem.* = i + i;
}
ok(array[0] == 0);
ok(array[1] == 2);
ok(array[2] == 4);
ok(array[3] == 6);
}
i
はusize
型であることに注意して下さい。
C言語を経験している方は、インデックスをインクリメントしながら配列要素をアクセスする方法に馴染みがあるかもしれません。しかしそのような配列のアクセス方法は間違いを犯しやすいため、Zenでは非推奨です。
要素数が型の一部であるため、次のように、インデックスアクセス時に要素数以上のインデックスを指定すると、コンパイルエラーになります。
const array = [4]i32{ 1, 2, 3, 4 };
ok(array[4] != 4);
error[E04028]: index is out of bounds
ok(array[4] != 4);
~
ただし、コンパイルエラーとなるのは、インデックスがコンパイル時計算可能な場合です。インデックスが実行時にしか計算できない場合、安全性保護付き未定義動作により、パニックが発生します。
test "invalid index at runtime" {
var array = [_]i32{ 1, 2, 3, 4 };
var index: usize = 4;
array[index] = 0;
}
index out of bounds
array[index] = 0;
^
コンパイル時計算可能な配列に対しては、配列の連結をしたり、配列の積を計算することができます。
配列を連結するときは、++
演算子を使用します。連結する配列は、双方ともコンパイル時計算可能である必要があります。
examples/ch02-primitives/src/arrays.zen:74:82
const equalSlices = std.testing.equalSlices;
test "concatenates arrays" {
const array1 = [_]i32{ 1, 2 };
const array2 = [_]i32{ 3, 4 };
const concatenated = array1 ++ array2;
const expected = [_]i32{ 1, 2, 3, 4 };
equalSlices(i32, &concatenated, &expected);
}
equalSlices
は、2つの配列が一致比較を行うテスト用関数です。正確にはスライスの一致比較を行います。スライスについては、次の節で説明しますので、ここでは単純に2つの配列を比較している、と考えて下さい。
配列の積を計算するときは、**
演算子を使用します。array ** num
で、配列array
をnum
回連結した配列を生成します。連結する配列は、双方ともコンパイル時計算可能である必要があります。
examples/ch02-primitives/src/arrays.zen:84:89
test "multiplies array" {
const multiplied = [_]i32{ 1, 2 } ** 2;
const expected = [_]i32{ 1, 2, 1, 2 };
equalSlices(i32, &multiplied, &expected);
}
配列の積は配列の要素を0
でクリアする場合にも利用できます。
examples/ch02-primitives/src/arrays.zen:91:97
test "zero clear array" {
const zeros = [_]i32{0} ** 100;
ok(zeros.len == 100);
for (zeros) |zero| {
ok(zero == 0);
}
}
配列の連結と配列の積は、コンパイル時計算可能な場合に限り利用できます。実行時に長さが変化する可変長配列は、標準ライブラリのArrayList
を使用します。ArrayList
については、9章 containerを参照して下さい。
多次元配列は配列をネストすることで作成できます。
examples/ch02-primitives/src/arrays.zen:99:114
test "two-dimensional arrays" {
const matrix = [3][4]i32{
[4]i32{ 0, 1, 2, 3 },
[4]i32{ 4, 5, 6, 7 },
[4]i32{ 8, 9, 10, 11 },
};
ok(@TypeOf(matrix) == [3][4]i32);
// `matrix`は`[4]i32`の配列を3つ持つ
ok(matrix.len == 3);
// インデックス1つで中の`[4]i32`配列にアクセス
ok(@TypeOf(matrix[0]) == [4]i32);
ok(matrix[0].len == 4);
// インデックス2つで`i32`にアクセス
// インデックスは外側から順番に書く
ok(matrix[1][2] == 6);
for
をネストすることで、全要素に順番にアクセスできます。
examples/ch02-primitives/src/arrays.zen:116:122
var sum: i32 = 0;
for (matrix) |array| {
for (array) |elem| {
sum += elem;
}
}
ok(sum == 66);
多次元配列においても要素数の推論が可能です。
examples/ch02-primitives/src/arrays.zen:125:133
test "inference of two-dimensional arrays" {
// 一番外の配列要素数は推論可能
const matrix = [_][4]i32{
// 内側の配列要素定義では推論可能
[_]i32{ 0, 1, 2, 3 },
[_]i32{ 4, 5, 6, 7 },
[_]i32{ 8, 9, 10, 11 },
};
}
次節で説明するスライスを使って、次のような多次元配列を作成できます。
examples/ch02-primitives/src/arrays.zen:135:144
test "array of slice" {
const matrix = [_][]i32{
&[_]i32{ 0, 1, 2 },
&[_]i32{ 3, 4, 5, 6, 7 },
&[_]i32{ 8, 9 },
&[_]i32{ 10, 11, 12, 13, 12, 15, 16 },
};
ok(@TypeOf(matrix) == [4][]i32);
}
配列型の特殊な型として配列の終端値を指定することができます。配列型を指定する際に []
の中に :
に続けて終端値を指定します。
例えば、要素数が 5
、終端値が 10
、要素型が u32
の配列の型は [5:10]u32
となります。
特に Zen の文字列リテラルは 0
を終端値とした u8
の配列となります。厳密には *[5:0]u8
のようなポインタとなります。これにより、C言語のライブラリ関数に文字列を渡す場合には変換をせずに使用することができます。
終端値付き配列の要素数 len
は、終端値の分を含まない値となります。メモリ上は要素数 + 1 の位置に終端値が格納されます。
終端値には基本的にアクセスできません。例えば、var x: [5:10]u32
の配列に対して x[5]
にアクセスするようなコードを書くとコンパイルエラーとなります。また、実行時に終端値にアクセスした場合は安全性保護付き未定義動作となります。
これらについて次のコードで説明します。
examples/ch02-primitives/src/arrays.zen:146:160
test "sentinel-terminated arrays" {
const x100 = [_:100]u32{ 1, 2, 3 };
ok(@TypeOf(x100) == [3:100]u32);
ok(x100.len == 3);
const string = "hello";
ok(@TypeOf(string) == *[5:0]u8);
// error[E04028]: index is out of bounds
//ok(x100[x100.len] == 100);
// 実行時に安全性保護付き未定義動作
//var index = x100.len;
//ok(x100[index] == 100);
}
第2章 文字列、第14章 Cとのインタフェース も参照してください。
☰ 人の生きた証は永遠に残るよう ☰
Copyright © 2018-2020 connectFree Corporation. All rights reserved. | 特定商取引法に基づく表示
Zen, the Zen three-circles logo and The Zen Programming Language are trademarks of connectFree corporation in Japan and other countries.