D言語基礎文法最速マスター

注意!

ふるいので書き直しました.https://gist.github.com/2470712 にある新しい方をごらんください!

以降古い版

他の言語をある程度知っている人はこれを読めばD言語の基礎をマスターでき,D言語の氷山の一角くらいは知ることができると思います.対象バージョンはdmd 2.040です.

1. 基礎

ソースファイル

ASCIIかUTFしか受け付けません.それ以外の文字コードで書くとコンパイルエラーになります.

main

D言語のmainはCとは違い以下のようなシグネチャです.

void main();
void main(string[] args);

リターンコードについては処理系がよしなにやってくれます.

表示 (write(ln), writef(ln))

標準ライブラリにあるstd.stdioを使います.

import std.stdio;
...
write(1);          // 改行無し
writeln(1);        // 改行有り
writef("%02d", 1); // フォーマット指定
変数の宣言

D言語では"型 変数名"というように宣言します.

int a;        // デフォルト値で初期化される.intは0
int a = void; // 初期化されず,Cなどと同じくゴミが入っている

また,初期化子から型推論するためのautoという予約語があります.

auto a = "auto";   // aはimmutable(char)[]型

他の修飾子がある場合も型推論が効きます.

const a = "const";
データ型

値型と参照型があります.以下は値型の一例です.

// ブール値
bool flag;

// 符号なし8bitのUTF-8文字(マルチバイトじゃないので注意)
char c;

// 数値はCとは違いサイズ固定です.
byte   num1;  // 8bit
short  num2;  // 16bit
int    num3;  // 32bit
double num4;  // 64bit

// 静的配列
int[5] arr;

// 構造体
Random random;

参照型は複合的なデータ構造などが当てはまります.

// 動的配列
int[] arr;

// オブジェクト
Object obj;

// デリゲート
void delegate(void) action;
コメント

4種類あります.

// 一行コメント

/*
   複数行コメント
 */

/**
   DDocコメント
 */

/+ /+
   ネスト可能複数行コメント
 +/ +/
プログラムの実行

コンパイル

$ dmd foo.d
$ ./foo

runオプション使うと実行ファイルなどを生成せずに実行できます.

$ dmd -run foo.d

2. 数値

整数.uがつくと符号なしになります.

long  num = -1;          // 符号つき
ulong num = 100_000_000; // 符号なし

浮動小数点数

double num = 1.234;
real   num = 5.678;  // 現状唯一のハードウェア依存(x86 CPUなら80bit)

複素数も使えたりします.

creal num = 1 + 2i;
num += 3i;
writeln(num.re, num.im);  // reが実部(1),imが虚部(5)
四則演算
// numはintとする
num = 1 + 1;
num = 1 - 1;
num = 1 * 2;
num = 5 / 2;  // 2
num = 5 % 2;  // 1

演算子のどちらかが浮動小数点数の場合,結果も浮動小数点数になります.

// numはdoubleとする
num = 5.0 / 2;  // 2.5(numがintなどの整数型だとコンパイルエラー)
インクリメントとデクリメント

勿論あります.

i++;
--i;

3. 文字列

文字列はダブルクォートで囲みます.ダブルクォートの中では\t(タブ)や\n(改行)などの特殊文字を利用することができます.

string str1 = "abc";
string str2 = "a\tbc\n";

D言語での文字列は4で述べる配列の一種に過ぎません(stringはimmutable(char)[]のaliasです).またポストフィックスをつけることでリテラルの型を指定できます.

string  str3 = "hello"c  // 各文字はchar型
wstring str4 = "hello"w  // 各文字はwchar型
dstring str5 = "hello"d  // 各文字はdchar型
文字列操作
// 結合
auto str = "aaa" ~ "bbb";

// 長さ(バイト)
auto length = "abcdef".length;

// 切り出し
auto substr = "abcd"[0..2];  // "ab"

/* これ以降のものはstd.stringが必要です */

// 分割
auto record = "aaa,bbb,ccc".split(",");

// 検索
auto idx = "abcd".indexOf("bc");  // 見つかった場合はその位置,見つからなかった場合は-1

4. 配列

配列は「[]」を使います.静的配列と動的配列がありますが,よく使われる動的配列について書きます.

int[] arr = [100, 200, 300];

宣言ではCとは違い前置形式となります.

要素の参照と代入
// 参照
a = arr[0];
b = arr[1];
c = arr[5];  // Error! 要素数より多いと例外が投げられる

// 代入
arr[0] = 1;
arr[1] = 2;
arr[5] = 5;  // 参照と同じく
要素の個数
len = arr.length;

// 要素を増やす(増えた分はデフォルト値で埋められる)
arr.length += 10;
配列の操作

std.arrayを使うとD言語での標準的なインターフェイス(Range)が利用できます.

import std.array;

auto arr = [1, 2, 3];

// 先頭を取得
auto a = arr.front;  // aは1

// 先頭を削除
arr.popFront;        // arrは[2, 3]

// 先頭に追加(push系がないのでinsertで)
arr.insert(0, 5);    // arrは[5, 2, 3]

// 末尾を取得
auto b = arr.back;   // bは3

// 末尾を削除
arr.popBack;         // arrは[5, 2]

// 末尾に追加
arr ~= 9;            // arrは[5, 2, 9]

popFrontやpopBackで値が返らないのは例外安全のためです.

ベクトル演算

いちいちループとか使う必要ありません.[]を使うことでベクトル演算できます.

auto a = [1, 2, 3];
a[] += 10;  // [11, 12, 13]

5. 連想配列

連想配列も「[]」を使います.キーと値を:で区切ります.

int[string] hash = ["a" : 1, "b" : 2];
要素の参照と代入
// 参照
hash["a"]  // 1
hash["b"]  // 2
hash["z"]  // 配列と同じく例外が投げられる

// 代入
hash["c"] = 5
hash["d"] = 7
連想配列の操作
// キーの取得
hash.keys;   // ["a", "b", "c", "d"]

// 値の取得
hash.values; // [1, 2, 5, 7]

// キーの存在確認
auto val = "a" in hash;  // valには1へのポインタ,なければnull

// ハッシュのペアの削除
hash.remove("a");

6. 制御文

if文
if (cond) {
    // do something
}
if 〜 else文
if (cond) {
    // do something
} else {
    // do something
}
if 〜 else if 文
if (cond) {
    // do something
} else if (cond) {
    // do something
}
switch文

Cとは違い,文字列が使えたりcaseを並べて書くことが出来ます.

switch (command) {
  case "foo", "bar":
    // do something
    break;
  case "baz":
    // do something
    break;
  default:
}
while文
uint i;
while (i < 5) {
    // do something
    ++i;
}
for文
for (uint i; i < 5; i++) {
    // do something
}
foreach文

配列や,opApply/Rangeインターフェイスを実装しているオブジェクトを処理出来ます.

foreach (elem; arr) {
    // do something
}

7. 関数/デリゲート

関数はCと同じようなものですが色々と指定できます.xはconstな値として入力を表し,refは参照として操作することを表します.lazyは遅延評価(zが関数内で使われるまで評価を遅延させる)を行います.

nothrow void foo(in int x, ref int y, lazy int z = 0)
{
    // do something
}

デリゲートはネストされた関数などが該当します.これはクロージャの働きもします.

uint delegate() createCounter()
{
  uint count;
  return { return ++count; };  // {}はdelegateリテラル(引数がないので()は省略)
}

auto counter = createCounter();
writeln(counter());  // 1

8. ファイル入出力

std.stdioにあるFileを使います.以下はコピーの例です.

auto fin  = File("orig.txt");       // デフォルトは読み込み
auto fout = File("copy.txt", "w");  // 書き込みモードでオープン

foreach (line; fin.byLine)
    fout.write(line);

// Fileは参照カウントで管理されているので明示的なcloseは不要

知っておいた方がよい文法

D言語の真偽値

null,false,数値型の0,まだ割り当てられていない空の配列,は偽となります.最後の配列の挙動はどうにも怪しいため,配列関係はemptyを使って評価することをオススメします.

配列を第一引数に取る関数

第一引数が配列の場合にはシンタックスシュガーが用意されています.

void foo(int[] arr) {}

int[] arr = [1, 2, 3];

// 以下はどっちでもOK
foo(arr);
arr.foo;

std.arrayやstd.stringを使った関数がvar.methodのように呼べたのはこのためです.

クラス

Javaと同等の機能を提供しています(単一継承です).

class Person
{
  private:  // 以降はprivate
    string name_;

  public:   // 以降はpublic
    @property
    {
        string name() const { return name_; }
        void name(string name) { name_ = name; }
    }

    this(string name)  // コンストラクタはthis
    {
        name_ = name;
    }

    ...
}  // Cなどと違い;はいらない

その他にもinterface,PODとして扱えるstructやunion,演算子オーバーロードなどもあります.また,D言語GCを使ってメモリ管理しているため,newした後deleteする必要はありません(自らmallocなどした場合は勿論freeは必要です).

テンプレート

C++よりかはすっきり書けるようになってます.以下は階乗を計算するテンプレートです.

template factorial(int n)
{
  static if (n == 1)
    enum factorial = 1;
  else
    enum factorial = n * factorial!(n-1);
}

/* C++などとは違い<>を使わず!()を使う */
factorial!(5) // 120

勿論クラスや関数でも使えます.

例外処理

Mutex(変数m)を例に.まずはよく知られているtry - catch - finally.

lock(m);
try {
    // do something
} finally {
    unlock(m);
}

スコープガード文を使った方法もあります.解放処理がとても近くなり,コードが見やすくなります.

lock(m);
scope(exit) unlock(m);  // どんな理由であれ,スコープを出る時に呼ばれる
// do something

RAIIのような方法も可能です.scopeで宣言された変数はスコープを抜ける時に破棄され,デストラクタを呼び出します.

class Lock
{
    Mutex m_;

    this(Mutex m) { m_ = m; lock(m_); }

    ~this() { unlock(m_); }  // デストラクタは~this
}

scope myLock = new Lock(m);
// do something
Range

D言語の標準ライブラリは今このコンセプトを中心に開発されています.核となるstd.rangeに満たすべきインターフェイスが定義されています.

import std.algorithm;

auto arr = [1, 2, 3, 4, 5];

// 条件に合うものだけを選ぶ
filter!("a % 2 == 0")(arr);  // [2, 4]

// 条件に合うものを除く
remove!("a % 2 == 0")(arr);  // [1, 3, 5]

// 条件に合うものの個数を数える
count!("a % 2 == 0")(arr);   // 2

// 加工結果をRangeで返す
map!("a * a")(arr);          // [1, 4, 9, 16, 25]

// 降順にソートする
sort!("a > b")(arr);         // [5, 4, 3, 2, 1]

8で上げたFileなども含め,他のモジュールもRangeベースとなっています.

単体テスト

unittestで囲った所にテストをかけます.クラス内でも構わないので,より近くに書くのがD言語の作法です.

unittest 
{
    bool foo() { /* do something */ return flag; }
    assert(foo());
}

これはコンパイル時にunittestオプションを渡すことで実行されるようになります.

終わりに

旬が過ぎてしまった気はしますが書いてみました.template/契約/TLS/型システムあたり含め,全然魅力を書けてない気もしますが基本ということでまぁこんな感じで許して下さい.D言語は進化が早い言語なので,この記事もいつ使えなくなるのかちょっと心配だったりします…