構造体

  1. 最もシンプルな設計の一つは、2つのdcharメンバーを使用することだ:
    struct Card {
        dchar suit;
        dchar value;
    }
    D
  2. 2つのメンバーを並べて表示するだけで済む。
    void printCard(Card card) {
        write(card.suit, card.value);
    }
    D
  3. newSuit()という関数がすでに存在すると仮定すると、newDeck()は、各スーツに対してその関数を呼び出すことで実装できる。
    Card[] newDeck()
    out (result) {
        assert(result.length == 52);
    
    } do {
        Card[] deck;
    
        deck ~= newSuit('♠');
        deck ~= newSuit('♡');
        deck ~= newSuit('♢');
        deck ~= newSuit('♣');
    
        return deck;
    }
    D

    残りの作業は、スーツの文字と文字列の各値を組み合わせてスーツを構築する、次のnewSuit()によって実行できる。

    Card[] newSuit(dchar suit)
    in {
        assert((suit == '♠') ||
               (suit == '♡') ||
               (suit == '♢') ||
               (suit == '♣'));
    
    } out (result) {
        assert(result.length == 13);
    
    } do {
        Card[] suitCards;
    
        foreach (value; "234567890JQKA") {
            suitCards ~= Card(suit, value);
        }
    
        return suitCards;
    }
    D

    上記の関数は、プログラムエラーのリスクを軽減するために契約プログラミングを利用していることに注意。

  4. 2つの要素をランダムに交換すると、繰り返しごとにデッキはますますシャッフルされる。偶然同じ要素が選択される可能性はあるが、要素を自分自身と交換しても、デッキがよりシャッフルされる機会を逃す以外の効果はない。
    void shuffle(Card[] deck, int repetition) {
        /* 注釈: より良いアルゴリズムは、デッキの先頭から
         *       末尾まで順に要素を移動し、各要素を
         *       その時点から末尾までの要素の中からランダムに選択した要素と
         *       交換するものだ。
         *
         * より良い方法は、std.algorithmモジュールからrandomShuffle()を
         * 呼び出すことである。この関数は同じアルゴリズムを既に適用している。
         * main()内のコメントを参照して、
         * randomShuffle()の使用方法を確認する。 */
        foreach (i; 0 .. repetition) {
            // 2つの要素をランダムに選択する
            immutable first = uniform(0, deck.length);
            immutable second = uniform(0, deck.length);
    
            swap(deck[first], deck[second]);
        }
    }
    D

    上記の関数は、2つのrefパラメータの値を単に交換するstd.algorithm.swapを呼び出す。これは、次の関数と実質的に同じだ。

    void mySwap(ref Card left,
                ref Card right) {
        immutable temporary = left;
        left = right;
        right = temporary;
    }
    D

以下にプログラム全体を示す。

import std.stdio;
import std.random;
import std.algorithm;

struct Card {
    dchar suit;
    dchar value;
}

void printCard(Card card) {
    write(card.suit, card.value);
}

Card[] newSuit(dchar suit)
in {
    assert((suit == '♠') ||
           (suit == '♡') ||
           (suit == '♢') ||
           (suit == '♣'));

} out (result) {
    assert(result.length == 13);

} do {
    Card[] suitCards;

    foreach (value; "234567890JQKA") {
        suitCards ~= Card(suit, value);
    }

    return suitCards;
}

Card[] newDeck()
out (result) {
    assert(result.length == 52);

} do {
    Card[] deck;

    deck ~= newSuit('♠');
    deck ~= newSuit('♡');
    deck ~= newSuit('♢');
    deck ~= newSuit('♣');

    return deck;
}

void shuffle(Card[] deck, int repetition) {
    /* 注釈: より良いアルゴリズムは、デッキを
     *       最初から最後まで順に、各要素を
     *       その時点から最後までにある要素の中からランダムに選んだ要素と
     *       入れ替えることだ。
     *
     * std.algorithmモジュールからrandomShuffle()を呼び出すと
     * さらに良い。このモジュールは、
     * 同じアルゴリズムをすでに適用している。randomShuffle()の使用方法については、
     * main()のコメントを参照。 */
    foreach (i; 0 .. repetition) {
        // 2つの要素をランダムに選択する
        immutable first = uniform(0, deck.length);
        immutable second = uniform(0, deck.length);

        swap(deck[first], deck[second]);
    }
}

void main() {
    Card[] deck = newDeck();

    shuffle(deck, 100);
    /* 注釈: 上記のshuffle()の呼び出しの代わりに、
     *       次の行のようにrandomShuffle()を呼び出す方が
     *       良い:
     *
     * randomShuffle(deck);
     */
    foreach (card; deck) {
        printCard(card);
        write(' ');
    }

    writeln();
}
D
struct.solution.1