配列の共有
配列をラッピングして共有するためのクラスを作った。
なんでこんなものを作ったかと言うと、配列の共有のされ方が
ハンパだからである。
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); } } }