ポインタ

  1. パラメータがintのような値型の場合、引数は関数にコピーされる。参照パラメータを定義する推奨方法は、refとして指定することだ。

    別の方法は、パラメーターを実際の変数を指すポインタとして定義することだ。プログラムの変更部分はハイライト表示されている:

    void swap(int * lhs, int * rhs) {
        int temp = *lhs;
        *lhs = *rhs;
        *rhs = temp;
    }
    
    void main() {
        int i = 1;
        int j = 2;
    
        swap(&i, &j);
    
        assert(i == 2);
        assert(j == 1);
    }
    D
    pointers.solution.1

    プログラムの末尾のチェックは現在、パスする。

  2. NodeおよびListは、int型でのみ動作するように記述されている。これらの型は、名前の後に(T)を追加し、定義内の適切なintTに置き換えることで、構造体テンプレートに変換することができる。
    struct Node(T) {
        T element;
        Node * next;
    
        string toString() const {
            string result = to!string(element);
    
            if (next) {
                result ~= " -> " ~ to!string(*next);
            }
    
            return result;
        }
    }
    
    struct List(T) {
        Node!T * head;
    
        void insertAtHead(T element) {
            head = new Node!T(element, head);
        }
    
        string toString() const {
            return format("(%s)", head ? to!string(*head) : "");
        }
    }
    D
    pointers.solution.2

    Listは、あらゆる型で使用できるようになった。

    import std.stdio;
    import std.conv;
    import std.string;
    
    // ...
    
    struct Point {
        double x;
        double y;
    
        string toString() const {
            return format("(%s,%s)", x, y);
        }
    }
    
    void main() {
        List!Point points;
    
        points.insertAtHead(Point(1.1, 2.2));
        points.insertAtHead(Point(3.3, 4.4));
        points.insertAtHead(Point(5.5, 6.6));
    
        writeln(points);
    }
    D
    pointers.solution.2

    出力:

    ((5.5,6.6) -> (3.3,4.4) -> (1.1,2.2))
  3. この場合、リストの最後のノードを指す別のポインタが必要になる。新しい変数を管理するため、新しいコードは必然的に複雑になる:
    struct List(T) {
        Node!T * head;
        Node!T * tail;
    
        void append(T element) {
            /* 最後のノードの後にノードがないため、
             * 新しいノードのnextポインタを'null'に設定する。 */
            auto newNode = new Node!T(element, null);
    
            if (!head) {
                /* リストは空になった。新しいノードが
                 * 先頭になる。 */
                head = newNode;
            }
    
            if (tail) {
                /* このノードを現在の末尾の後に配置する。 */
                tail.next = newNode;
            }
    
            /* 新しいノードが新しい末尾になる。 */
            tail = newNode;
        }
    
        void insertAtHead(T element) {
            auto newNode = new Node!T(element, head);
    
            /* 新しいノードが先頭ノードになる。 */
            head = newNode;
    
            if (!tail) {
                /* リストは空になった。新しいノードが
                 * 末尾になる。 */
                tail = newNode;
            }
        }
    
        string toString() const {
            return format("(%s)", head ? to!string(*head) : "");
        }
    }
    D

    insertAtHead()の新しい実装は、実際にはもっと短くすることができる。

    void insertAtHead(T element) {
        head = new Node!T(element, head);
    
        if (!tail) {
            tail = head;
        }
    }
    D

    次のプログラムは、新しいListを使用して、奇数の値を持つPointオブジェクトを先頭に、偶数の値を持つPointオブジェクトを最後に挿入する。

    void main() {
        List!Point points;
    
        foreach (i; 1 .. 7) {
            if (i % 2) {
                points.insertAtHead(Point(i, i));
    
            } else {
                points.append(Point(i, i));
            }
        }
    
        writeln(points);
    }
    D

    出力:

    ((5,5) -> (3,3) -> (1,1) -> (2,2) -> (4,4) -> (6,6))