Skip to content

Instantly share code, notes, and snippets.

@repeatedly
Last active December 17, 2024 10:01

Revisions

  1. repeatedly revised this gist Apr 7, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion d_master.md
    Original file line number Diff line number Diff line change
    @@ -669,7 +669,7 @@ unittest

    # 終わりに

    template/契約/TLS/型システムあたり含め,全然魅力を書けてない気もしますが基本ということでまぁこんな感じで許して下さい.他に詳しく知りたい方はTwitterで #d_lang をつけてつぶやくか,以下のサイトを参考にしてみてください.この記事を必要であれば随時アップデートしていきます.
    template/契約/TLS/型システムあたり含め,全然魅力を書けてない気もしますが基本ということでまぁこんな感じで許して下さい.他に詳しく知りたい方はTwitterで #dlang をつけてつぶやくか,以下のサイトを参考にしてみてください.この記事を必要であれば随時アップデートしていきます.

    - [公式サイト](http://www.digitalmars.com/d/2.0/index.html)
    - [公式サイトの日本語訳](http://www.kmonos.net/alang/d/)
  2. repeatedly created this gist Apr 23, 2012.
    680 changes: 680 additions & 0 deletions d_master.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,680 @@
    他の言語をある程度知っている人はこれを読めばD言語の基礎をマスターでき,D言語の氷山の一角くらいは知ることができると思います.対象バージョンはdmd 2.059です.

    # 1. 基礎

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

    ## main

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

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

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

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

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

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

    ## 変数の宣言

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

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

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

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

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

    ```d
    const a = "const";
    ```

    ## データ型

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

    ```d
    // ブール値
    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;
    ```

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

    ```d
    // 動的配列 (スライス)
    int[] arr;
    // オブジェクト
    Object obj;
    // デリゲート
    void delegate(void) action;
    ```

    参照型と言っても実はポインタの値渡しみたいなものです.

    ## コメント

    4種類あります.

    ```d
    // 一行コメント
    /*
    複数行コメント
    */
    /**
    DDocコメント
    */
    /+ /+
    ネスト可能複数行コメント
    +/ +/
    ```

    ## プログラムの実行

    コンパイル.

    ```sh
    $ dmd foo.d
    $ ./foo
    ```

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

    ```sh
    $ dmd -run foo.d
    ```

    -runの依存関係解決などを強化したrdmdも同梱されています.また,--evalで簡単にコードを試せたりもします.

    ```sh
    $ rdmd foo.d
    $ rdmd --eval="writeln(10);"
    ```

    # 2. 数値

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

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

    浮動小数点数.

    ```d
    double num = 1.234;
    real num = 5.678; // ハードウェア依存(x86 CPUなら80bit)
    ```

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

    ```d
    creal num = 1 + 2i;
    num += 3i;
    writeln(num.re, num.im); // reが実部(1),imが虚部(5)
    ```

    この組み込み複素数はstd.complexで置き換えられる予定です(まだ期日は決まってません).

    ## 四則演算

    ```d
    // numはintとする
    num = 1 + 1;
    num = 1 - 1;
    num = 1 * 2;
    num = 5 / 2; // 2
    num = 5 % 2; // 1
    ```

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

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

    ## インクリメントとデクリメント

    勿論あります.

    ```d
    i++;
    --i;
    ```

    # 3. 文字列

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

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

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

    ```d
    string str3 = "hello"c // 各文字はchar型
    wstring str4 = "hello"w // 各文字はwchar型
    dstring str5 = "hello"d // 各文字はdchar型
    ```

    ## 文字列操作

    ```d
    // 結合
    auto str = "aaa" ~ "bbb";
    // 長さ(バイト)
    auto length = "abcdef".length;
    // 切り出し
    auto substr = "abcd"[0..2]; // "ab"
    /* これ以降のものはstd.stringが必要です */
    // 分割
    auto record = "aaa,bbb,ccc".split(","); // ["aaa", "bbb", "ccc"]
    // 検索
    auto idx = "abcd".indexOf("bc"); // 見つかった場合はその位置,見つからなかった場合は-1
    ```

    # 4. 配列

    配列は「[]」を使います.静的配列と動的配列がありますが,よく使われる動的配列について書きます(動的配列は実はスライスだったりします.詳しくは[この記事](http://dlang.org/d-array-article.html)を参照).

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

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

    ## 要素の参照と代入

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

    ## 要素の個数

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

    ## 配列の操作

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

    ```d
    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で値が返らないのは例外安全のためです.

    ## ベクトル演算

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

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

    # 5. 連想配列

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

    ```d
    int[string] hash = ["a" : 1, "b" : 2];
    ```

    ## 要素の参照と代入

    ```d
    // 参照
    hash["a"] // 1
    hash["b"] // 2
    hash["z"] // 配列と同じく例外が投げられる
    // 代入
    hash["c"] = 5
    hash["d"] = 7
    ```

    ## 連想配列の操作

    ```d
    // キーの取得
    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文

    ```d
    if (cond) {
    // do something
    }
    ```

    ## if ~ else文

    ```d
    if (cond) {
    // do something
    } else {
    // do something
    }
    ```

    ## if ~ else if 文

    ```d
    if (cond) {
    // do something
    } else if (cond) {
    // do something
    }
    ```

    ## switch文

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

    ```d
    switch (command) {
    case "foo", "bar":
    // do something
    break;
    case "baz":
    // do something
    break;
    default:
    }
    ```

    ## while文

    ```d
    uint i;
    while (i < 5) {
    // do something
    ++i;
    }
    ```

    ## for文

    ```d
    for (uint i; i < 5; i++) {
    // do something
    }
    ```

    ## foreach文

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

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

    要素を弄りたかったらrefをつけます.

    ```d
    foreach (ref elem; arr) {
    // do something
    }
    ```

    # 7. 関数/デリゲート

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

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

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

    ```d
    uint delegate() createCounter()
    {
    uint count;
    return { return ++count; }; // {}はdelegateリテラル(引数がないので()は省略)
    }
    auto counter = createCounter();
    writeln(counter()); // 1
    ```

    2.059から以下のようなラムダシンタックスがあります.

    ```d
    void f(uint delegate(uint) d)
    {
    writeln(d(10));
    }
    void main()
    {
    f((x) { return x + 2; });
    f(x => x + 2); // ラムダシンタックス(上のと等価)
    }
    ```

    ## CTFE(Compile Time Function Excecute)

    D言語ではコンパイル時に色々処理をすることが出来ます.クラスの生成も出来ますし,例外も投げれますし,もちろん数値計算や配列も操作出来ます.そのため,コンパイル時に定数を渡せば,それなりに色々な関数が動作します.
    たとえば,以下のような乱数生成も普通にコンパイル時に動きます.

    ```d
    ulong gen()
    {
    Random r;
    popFrontN(r, 1000);
    return r.front;
    }
    ```

    # 8. ファイル入出力

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

    ```d
    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を使って評価することをオススメします.

    ## クラス

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

    ```d
    class Person
    {
    private: // 以降はprivate
    string name_;
    public: // 以降はpublic
    @property
    {
    // @properyをつけるとname()ではなくnameで呼び出せるようになる
    // p.name
    string name() const { return name_; }
    // p.name = newName
    void name(string name) { name_ = name; }
    }
    this(string name) // コンストラクタはthis
    {
    name_ = name;
    }
    ...
    } // Cなどと違い;はいらない
    ```

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

    ## テンプレート

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

    ```d
    template factorial(int n)
    {
    static if (n == 1)
    enum factorial = 1;
    else
    enum factorial = n * factorial!(n - 1);
    }
    /* C++などとは違い<>を使わず!()を使う */
    factorial!(5) // 120
    ```

    このようにD言語ではコンパイル時用のstatic ifがあったりします.

    勿論クラスや関数(UFCSの項参照)でも使えます.

    ```d
    class Hoge(T)
    {
    T value;
    }
    ```

    ### テンプレートミックスイン

    単一継承のD言語では,クラス間で実装を共有する時はテンプレートを使います.

    ```d
    template HogeImpl()
    {
    uint hoge_;
    uint hogeTwice() { return hoge_ * hoge_; }
    }
    class A
    {
    mixin HogeImpl; // Aで定義したかのように使える
    }
    class B
    {
    mixin HogeImpl; // 同様
    }
    ```

    ## UFCS (Uniform Function Call Syntax)

    第一引数をオブジェクトのように扱えるシンタックスシュガーです.2.058までは配列のみでしたが,2.059からintとかでも使えます.

    ```d
    void foo(T)(T obj) {}
    // 本来はfoo([1, 2, 3])とか
    [1, 2, 3].foo();
    5.foo();
    2.5f.foo();
    ```

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

    ## 例外処理

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

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

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

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

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

    ```d
    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に満たすべきインターフェイスが定義されています.以下はstd.rangeベースのアルゴリズムの例です.

    ```d
    import std.algorithm;
    auto arr = [1, 2, 3, 4, 5];
    // 条件に合うものだけを選ぶ
    filter!("a % 2 == 0")(arr); // [2, 4]
    // '"a % 2 == 0"'の代わりに'a => a % 2 == 0'でもOK.delegateと文字列両方受け付ける
    filter!(a => a % 2 == 0)(arr); // [2, 4]
    // 条件に合うものを除く
    remove!("a % 2 == 0")(arr); // [1, 3, 5]
    // 加工結果をRangeで返す
    map!("a * a")(arr); // [1, 4, 9, 16, 25]
    // 降順にソートする
    sort!("a > b")(arr); // [5, 4, 3, 2, 1]
    ```

    基本的に,これらのアルゴリズムが返すのはarrの型ではなく,それぞれの計算を隠蔽したRangeオブジェクトです.これによって,Rangeは基本的には計算を遅延し,それぞれのRangeをつなげても,なるべく中間のオブジェクトを作らないようにしています.
    配列として結果を返して欲しい時はstd.array.arrayを使います

    ```d
    import std.array;
    array(map!("a * a")(arr));
    ```

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

    ## 単体テスト

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

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

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

    # 終わりに

    template/契約/TLS/型システムあたり含め,全然魅力を書けてない気もしますが基本ということでまぁこんな感じで許して下さい.他に詳しく知りたい方はTwitterで #d_lang をつけてつぶやくか,以下のサイトを参考にしてみてください.この記事を必要であれば随時アップデートしていきます.

    - [公式サイト](http://www.digitalmars.com/d/2.0/index.html)
    - [公式サイトの日本語訳](http://www.kmonos.net/alang/d/)
    - [dusers: 日本でのポータルサイト](http://dusers.dip.jp/)
    - [わかったつもりになるD言語](http://www.kmonos.net/alang/wnd/)(Phobosなどに古い記述がありますが,templateなどの内容が参考になります)

    ##
    - [The D Programming Language](http://www.amazon.co.jp/D-Programming-Language-Andrei-Alexandrescu/dp/0321635361)