Nimの構文についての勉強の4回目になります。
今回は、標準で用意されているNimの様々なデータ型について調べていきます。
少し長いので、前編・後編に分けようと思います。
今回は前編です。
また、後編では、オブジェクト指向について、NimにはClassの構文はありませんが、これから紹介するデータ型を駆使して、オブジェクト指向のプログラミングを書くことはできますので、最後にその辺りもやっていこうと思います。
Nimでは type キーワードを用いることで、独自の型を作成することが出来ます。
50
type文は通常、ベースとなった型からの代入は可能ですが、distinctキーワードを用いる事で、ベースとなった型と違う型であることを明示的に示す事ができます。
Nimでの列挙型の定義は以下の通りとなります。
TAKO
列挙型の各要素は改行もしくはカンマで区切って定義します。
また、外の変数との重複を避けるため、完全修飾([型名].[要素名])での表記もできます。
次の例は、上のコードと同じです。
各要素は内部的には int型の整数で、デフォルトでは 0 から順番に採番されています。
値を明示的に指定することも可能ですが、その場合は他の要素と値が被らないこと、及び先頭から昇順に並んでいる必要があります。
先にも述べたとおり各要素は内部的には整数なので、整数同様比較演算が使えます。
また ordプロシージャを用いることで、整数値に変換することが出来、
先頭に $ を付与することで、要素名を文字列に変換することが出来ます。
すごいタコ!! 10 UNI
あと、値には数字ではなく文字列も指定できるようです。
烏賊 0
序列型という型があるわけではなく、列挙型(enum), 整数型(interger), char型, boolean型を総じて序列型と呼んでいます。
(この後説明するサブレンジについても序列型になります)
序列型は以下の操作が出来ます。
ord(x) | xを表す数値を返します。
intで使用してもあまり意味がないので、enum や char, boolean で使用するものだと思います。
実行結果
true 1 |
inc(x) | x を 1増加させます。 |
inc(x, n) | x を n増加させます。 |
dec(x) | x を 1減少させます。 |
dec(x, n) | x を n減少させます。 |
succ(x) | x を 1増加した値を返します。 |
succ(x, n) | x を n増加した値を返します。 |
pred(x) | x を 1減少した値を返します。 |
pred(x, n) | x を n減少した値を返します。 |
inc と succ(dec と pred)の違いですが、
inc, dec は変数 x の内容を直接変更します。
対して succ, pred は x の内容は変更せずに結果を戻り値として返します。
ちなみに、enum で値指定して定義した際、連続した数値でないと、ord 以外の命令はコンパイルエラーになるようです。
range を用いることで数値の範囲を制限できます。
範囲外の値を設定しようとした場合、コンパイルエラーもしくはランタイムエラーになります。
D:\Projects\nim\nimtest01\test01.nim(6) test01 D:\devtools\nim\lib\system\fatal.nim(49) sysFatal Error: unhandled exception: value out of range: 11 notin 0 .. 10 [RangeError]
普通に range[0..10] とか書くと、intのサブレンジが出来るのですが、他の型で作りたい場合はどうするのが良いのだろう?
一応、次のような感じで出来たが、もう少しスマートな書き方はないものか?
1
集合型は、集合を扱うための型になります。
例えば次の例では、char型の値の範囲を持つ集合を定義し、'a'~'z' のアルファベットと、'0'~'9' の数値を要素として持つ集合を作成しています。
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}
集合型で使用できる演算は以下の通りです。
A + B | 集合の和 |
A * B | 集合の積 |
A - B | 集合の差 |
A == B | 2つの集合が等しい |
A <= B | A は B に含まれる、もしくは等しい |
A < B | A は B に含まれるが等しくない |
e in A | 要素 e は A に含まれる |
e notin A | 要素 e は A に含まれない |
contains(A, e) | 要素 e は A に含まれる(e in A と同じ?) |
card(A) | A の要素数 |
incl(A, elem) | A = A + {elem} と同じ |
excl(A, elem) | A = A - {elem} と同じ |
A + B: {0, 1, 2, 3, 4} A * B: {2} A - B: {0, 1} A == B: false C <= A: true C < A: false 1 in A: true 1 notin A: false contains(A, 1): true incl(A, 4): {0, 1, 2, 4} excl(A, 1): {0, 2, 4}
set型は内部でビットフィールドで定義されているため集合の要素数に制限があり、現時点では 2^16個(65,536個)の要素までしか表現出来ないそうです。
それでも 65,536bit = 8,192byte なので、(最大要素で定義した場合)1つの変数に8Kb以上のメモリを要することになるので、不用意にたくさん使うとメモリ不足などの問題もおきそうです。
ちなみに、型推論を用いた場合最大要素数で定義されるようです。
8192
小さな型を明示的に指定して定義した場合、変数のサイズが少なくなります。
32
「必要な要素数は2,3個なんで、1byteのビットフィールド作りたいんだけど...」
という場合は、enum と一緒に用いると定義できるようです。
enum の要素数が8個以下の場合、1byteのビットフィールドが作成されます。
1 2
Nimにおける配列(array)は、サイズ変更不可の固定長コンテナとなります。
配列は array[(要素数), 型] で作ることが出来ます。
下の例では 0~6 のインデックスを持つ、int型の array を作成しています。
[1, 1, 9, 2, 2, 9, 6] 9
多次元配列は、以下の様に定義できます。
7
配列操作用のプロシージャとしては以下のようなものがあります。
len(x) | 配列 x の長さ |
low(x) | 配列 x のインデックスの下限 |
higi(x) | 配列 x のインデックスの上限 |
len(x): 4 len(x[0]): 3 low(x): 0 high(x): 3 high(x[0]): 2
配列のインデックスや型には enum を使用することも出来ます。
[y2, y3, y1, y1]
動的配列型(seq)は、配列に似ていますが、インデックスの範囲を動的に変更することが出来ます。
seq は @[] で初期化します(定義しただけでは nil となっているため使えません)。
seq は arrayで使用出来るプロシージャ以外にも以下のものが使用できます。
newSeq[N](x) | インデックス数が x で N型の動的配列を作成します。 |
newSeqOfCap[N](x) | インデックス数は 0 だが、x 分のキャパシティを持ったN型の動的配列を作成します。 |
setLen(x) | 配列のインデックス数を x にします。 |
add(a) | 配列の末尾に要素 a を追加します。 |
insert(a, x) | 配列の x 番目に要素 a を追加します。 |
delete(x) | x 番目の要素を削除します。 削除後も配列の並び順は保証されます。 |
del(x) | x 番目の要素を削除します。 削除後は配列の並びは保証されません。 delete と比較してこちらの方が処理速度が早いのだと思われます。 |
pop() | 末尾の要素を取得して、削除します。 |
x & y | 動的配列 x と y を結合します。 |
x.add(5): @[10, 0, 0, 5] x.insert(6, 1):@[10, 6, 0, 0, 5] x.delete(2): @[10, 6, 0, 5] x.del(0): @[5, 6, 0] x.pop(): 0 x &= @[7, 8]: @[5, 6, 7, 8]
オープン配列はプロシージャのパラメータとしてのみ使用することが出来ます。
配列をプロシージャのパラメータとして渡す場合、定義と呼出で、配列の型・インデックスが一致していないとコンパイルエラーになるため、非常に使い勝手が悪いです。
openArray を用いると、その辺りを柔軟に対応できます。
配列の型は合っている必要がありますが、固定長・可変長・インデックスのサイズに関わらず呼出すことができます。
0 1 2 3 4 5
尚、openArray では多次元配列の受渡しは出来ないそうなので、ちょっと注意が必要です。
varargs パラメータは openArray と似ていますが、これは可変長引数を受け渡す際の、仕組みになります。
red blue green
また、以下のように第2パラメータに `$` を追加すれば、文字列の引数に数値を渡しても、文字列に自動変換してくれるようなのですが、他の使い方がよく分かりません。
red blue 333
スライスは、文字列や配列の一部分を切り取ったり、指定した範囲の変更を行うための構文になります。
kani[3..6] : Tako kani : IkaカニUniEbi ebi : [1, 4, 3] tai : @[1, 4, 5, 6, 3]
結構長くなったので、今回はここまでにします。 次回、オブジェクト型以降の説明は後編 に続きます。