D言語基礎文法最速マスター
注意!
ふるいので書き直しました.https://gist.github.com/2470712 にある新しい方をごらんください!
1. 基礎
main
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コメント */ /+ /+ ネスト可能複数行コメント +/ +/
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 }
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ベースとなっています.