ホーム‎ > ‎D言語入門‎ > ‎

D言語入門 - データ型

数学的対象を扱うときは、個々の対象より「それら全体がなす集合」に注目する。 数学科に進学してしばらく経ち、この考え方に慣れてきたと思います。 基本的にはデータ型は集合だと思ってください。 変数に元を対応させるには、まず変数がどの集合に属するかを宣言します。

データ型 T に属する変数 x を宣言するには

T x;
のようにします。 最初にとるべき値が決まっているときは
T x = ...;
上の ... の部分に値 (や、それを与える式) を書きます。 この値や式を初期化子といいます。

初期化子を指定して変数を初期化するときは、型名を auto キーワードで代用できます。

auto x = ...;
ただし ... の内容から x の型を推論できることが必要で、数値の与え方ではアルファベットを後置して推論を制御する方法があります。

整数

整数を表す型はたくさんあります。

まず ubyte, ushort, uint, ulong はそれぞれ1バイト、2バイト、4バイト、8バイトの非負整数を格納します。 1バイトは8ビットですから、これは2進法の桁数で 8, 16, 32, 64 桁となります。 よって、最大値は順に 2^8-1 = 255, 2^16-1 = 65535, 2^32-1 = 4294967295, 2^64-1 = 18446744073709551615 です。

これらから u を取り除いた byte, short, int, long は、負の値も許す整数を格納します。 最大値の半分より大きい値の代わりに負の数を使うことができます。 最大値は順に 2^7-1 = 127, 2^15-1 = 32767, 2^31-1 = 2147483647, 2^63-1 = 9223372036854775807 です。 最小値は絶対値がこれらより1大きく、負号をもつ整数です。

これだけたくさんの型があると迷ってしまいます。 実習用の環境ではメモリを大量に積んでいるので、long にしておけばよいでしょう。 long で不足する時は ulong にするだけで間に合うことはまれで、後述の BigInt の出番かもしれません。

この型の変数を宣言するには、以下のようにします。

byte a; // 整数は0で初期化される
ubyte b = 127;
short c;
ushort d;
auto e = 10; // int と推論される
auto f = 10U; // uint と推論される
auto g = 10L; // long と推論される
auto h = 10UL; // ulong と推論される
ulong i = 10; // 型が指定されていればそちらに合わせるので UL を付けなくても ulong になる

浮動小数点数

実数なんてものはありません。 あるのは浮動小数点数です。 浮動小数点数の構造を詳しく説明するのは大変なので、詳しくは Wikipedia などを読んでください。 4バイトの float では精度不足になることが多いので、8バイトの double を使うことをおすすめします。 他に、環境依存ですが10バイトであることもあるreal というのがあります。

変数の初期化に関して注意が必要です。 宣言だけして自分で初期化しなかった場合、初期値は nan (Not A Number) という値になり、代入を除く任意の演算の結果が nan になります。

double a; // nan 初期化
double b = 0.0; // 自分で初期化
auto c = 0.0; // double と推論される
float d = 0.0;
auto e = 0.0F; // float と推論される
auto f = 0.0L; // real と推論される

配列

T に対し T[] は型になります。 T 型の値を (コンピュータが許す限りにおいて) 任意の有限個保持することができます。 宣言すると最初は空です。

long[] x;
配列の末尾に要素を加えるには ~= を使います。
ulong[] favorite; // 僕の好きな自然数を格納するための配列を定義
favorite ~= 0; // 0 は自然数
favorite ~= 1; // 1 も自然数
配列と配列とを連結するにも ~ を使います。
auto x = [1, 3, 5];
auto y = [2, 4, 6];
auto z = x ~ y; // [1, 3, 5, 2, 4, 6]
配列の初期化は簡単にできます。 配列から [何番目] と自然数を指定して要素を取り出すことができ、また要素へ代入できます。 0は自然数なので、配列 x の最初の要素は x[0] です。
ulong[] primes = [2, 3, 5, 7, 11, 13, 17, 19, 21]; // 最初の10個の素数
primes[9] = primes[1] * primes[3] + primes[0]; // 間違えたのでトリッキーな修正
1つずつ加えていくのもいっぺんに初期化するのも大変なら、長さを決めておいてループで代入しましょう。
ulong x = [0, 1, 4, 9, 16];
x.length = 1000; // 長さを1000にする。新しく増えた要素はすべて0で初期化される。
foreach (i; 5..x.length) // すでに5個の要素を決めてあるので、5番目から先を処理すればよい。
{
    x[i] = (i * i) % 997;
}

配列の機能

配列のスライス という非常に便利な機能があります。 また、最後の要素 x[x.length-1] や最後までのスライス x[c..x.length] を簡単に書けるように 配列の長さ を配列のインデクスにおける $ で代用できるようになっています。

bool search(long[] haystack, long needle)
{
    if (haystack.length == 0) // 空配列には入っていない
    {
        return false;
    }
    if (haystack[$-1] == needle) // 末尾にあるかどうかチェックする
    {
        return true;
    }
    return haystack[0..$-1].search(needle); // 末尾を除いた残りにあるかどうかチェックする
}
配列の各要素に対する操作をまとめて行う機能 もあります。
auto x = f(); // 何か数値の配列を返す関数を呼び、その結果を x に代入
x[] += 1; // x の要素すべてに1を加える
これは添え字にわたるループ (foreach range 文) で代入する
auto x = f();
foreach (i; 0..x.length)
{
    x[i] += 1;
}
や、配列などにわたる foreach 文 で ref パラメータを使った
auto x = f();
foreach (ref e; x)
{
    e += 1;
}
と等価です。

文字列

文字列は文字 char の配列ですが、個々の文字を書き換えられない点で異なります。

char[] x = ['C', '-', 'L', 'a', 'n', 'g'];
string y = ['D', '-', 'L', 'a', 'n', 'g'];
x[0] = 'D'; // OK
y[0] = 'C'; // error!
文字を表すにはシングルクオートを使います。 文字列の初期化にはダブルクオートを使う構文が用意されています。
char[] x = "C-Lang"; // error!
string y = "D-Lang"; // OK

その他の例

型は組み込みのものだけでなく、struct / class などを使って作ることができます。 標準ライブラリに入っている、数学に関係のありそうな型を紹介します。

複素数

std.complex をインポートすると複素数型を使うことができます。 といっても複素数なんてものはありません。 あるのは浮動小数点数を実部および虚部としてもつ型です。 浮動小数点数 double の上に作られた複素数型は Complex!double という変わった名前をしています。 このような型の名前を書かなければならないときは書くか他のサボり方をするのですが、宣言と同時に初期化するなら実部と虚部をとって複素数を返す関数 complex の結果を auto で受ければ解決します。また、関数の返り型を Comple!double にする場合も、auto を書けば大丈夫です。

import std.complex;
import std.stdio;

void main()
{
    auto z = complex(2.0, 1.0); // 2+i
    "%s ^ 2 = %s".writefln(z, z*z); // prints "2+1i ^ 2 = 3+4i"
    "Re %s = %s".writefln(z, z.re); // prints "Re 2+1i = 2"
    "Im %s = %s".writefln(z, z.im); // prints "Im 2+1i = 1"
}

auto f(Complex!double z)
{
    return 1 / z;
}

alias Complex!double cdouble; // 型に別名をつける

cdouble g(cdouble z)
{
    return z * z;
}

Complex!T h(T)(Complex!T z) // 型パラメータを使う
{
    return z + 1 / z;
}

長整数

std.bigint が長整数型を定義しています。

import std.bigint;
import std.stdio;

void main()
{
    BigInt x = 2;
    foreach (i; 0..16)
    {
        x *= x;
        x.writeln();
    }
}
これを実行すると表示される数たちは順に 4, 16, 256, 65536, 4294967296, 18446744073709551616, 340282366920938463463374607431768211456, えっと桁数が倍々になって大変なのでこの辺でやめておきます。
Comments