配列の共有

配列をラッピングして共有するためのクラスを作った。
なんでこんなものを作ったかと言うと、配列の共有のされ方が
ハンパだからである。

    char[] ar1 = "ABC";
    char[] ar2 = ar1;           // 配列を代入
    assert(ar1.ptr == ar2.ptr); // この時点で2つの配列は同じもの
    
    ar1[0] = 'a';               // 配列要素をいぢくる
    ar2[2] = 'c';
    assert(ar2[0] == 'a');      // 共有されてるので
    assert(ar1[2] == 'c');      // もう一方も変わってる
    
    ar1 ~= 'Z';                 // 配列に要素を追加
    assert(ar1.ptr != ar2.ptr); // もはや共有されていない

    ar1[0] = 'A';               // 値を変えても
    ar2[2] = 'C';
    assert(ar2[0] == 'a');      // もう一方の要素は
    assert(ar1[2] == 'c');      // もとのまま

といった具合。

Copy On Write とはそーいったものかも知れないが、やはり直感に反していてキモい。共有していると思っていたものがあるときパッと2つのものになるちうのは。

こうした挙動を避けるためには、最初から dup を使って2重化しておけばよい

    ar2 = ar1.dup;

では、要素が追加されたり削除されても共有したままでいたい場合には?
クラスで包んであげるしかない。

というわけで作ってみたが、まだ不完全。
toHash と opCmp の実装がショボいので、基本型以外の要素を持つものは、連想配列のキーに使えない。
この SharedArray にありえる全ての型を内部要素に持たせようとすると、型汎用の toHash と opCmp が必要。(以前、汎用文字列化関数を作ったみたいな感じで)
まだメンドくさくてやっていない。
土日の間作るかどうかは未定。

module house.sharedarray;

import house.tostring;
import house.array;
import std.c.string;

/// Authors: rinset
class SharedArray(T) {

    /** attribute */
    private T[] array;

    /** constructor */
    this(T[] array) { this.array = array.dup; }

    /** factory */
    static SharedArray opCall(T[] array)
        { return new SharedArray(array); }

    /** override of Object */
    override {
        char[] toString() { return array.ToString(); }
        int opEquals(Object o) {
            if (SharedArray rhs = cast(SharedArray)o) {
                return this.array == rhs.array;
            }
            return 0;
        }
        int opCmp(Object o) {
            if (SharedArray rhs = cast(SharedArray)o) {
                auto len = (array.length < rhs.length) ?
                    array.length : rhs.length;
                int result = memcmp(this.array, rhs.array, len);
                if (result == 0)
                    result = cast(int)this.length - cast(int)rhs.length;
                return result;
            }
            throw new Error(o.classinfo.name ~ " is not SharedArray");
        }
        hash_t toHash() {
            hash_t hash = 7;
            foreach (T value; array) {
                static if (is(typeof(T.toHash == Object.toHash)))
                    hash = hash * 19 + value.toHash();
                else
                    hash = hash * 19 + cast(hash_t)value * 17;
            }
            return hash;
        }
    }

    /** property of dynamic array */
    public {
        size_t length() { return array.length; }
        size_t length(size_t new_length) {
            return array.length = new_length;
        }
        T* ptr() { return this.array.ptr; }
        SharedArray dup() { return SharedArray(this.array); }
        SharedArray sort() {
            array.sort;
            return this;
        }
        SharedArray reverse() {
            array.reverse;
            return this;
        }
    }

    /** operators of dynamic array */
    public {
        T opIndex(int index) { return array[index]; }
        T opIndexAssign(T value, int index) {
            return array[index] = value;
        }

        SharedArray opSlice() { return this; }
        SharedArray opSlice(size_t from, size_t to) {
            return SharedArray(array[from..to]);
        }

        SharedArray opSliceAssign(T value) {
            array[] = value;
            return this;
        }
        SharedArray opSliceAssign(T value, size_t from, size_t to) {
            array[from..to] = value;
            return this;
        }

        SharedArray opCat(T value) {
            return SharedArray(this.array ~ value);
        }
        SharedArray opCatAssign(T value) {
            this.array ~= value;
            return this;
        }
        SharedArray opCat(T[] values) {
            return SharedArray(this.array ~ values);
        }
        SharedArray opCatAssign(T[] values) {
            this.array ~= values;
            return this;
        }

        SharedArray opCat(SharedArray other) {
            return SharedArray(this.array ~ other.array);
        }
        SharedArray opCatAssign(SharedArray other) {
            this.array ~= other.array;
            return this;
        }

        int opApply(int delegate(inout T) dg) {
            int result = 0;
            for (int i = 0; i < array.length; i++) {
                result = dg(array[i]);
                if (result) break;
            }
            return result;
        }

        int opApply(int delegate(inout int, inout T) dg) {
            int result = 0;

            for (int i = 0; i < array.length; i++) {
                result = dg(i, array[i]);
                if (result)
                break;
            }
            return result;
        }
    }

    /** utility */
    public {
        /** Returns: minimum value of collection */
        T min() { return house.array.min(this.array); }
        /** Returns: maximum value of collection */
        T max() { return house.array.max(this.array); }
    }
}