module pind.samples.ja.templates_more.templates_more_12; import std.stdio; import std.format; import std.string; /* 2次元int配列として機能する。 */ struct Matrix { private: int[][] rows; /* 行または列の範囲を表す。 */ struct Range { size_t begin; size_t end; } /* 行と列の範囲で指定されたサブマトリックスを * 返す。 */ Matrix subMatrix(Range rowRange, Range columnRange) { writeln(__FUNCTION__); int[][] slices; foreach (row; rows[rowRange.begin .. rowRange.end]) { slices ~= row[columnRange.begin .. columnRange.end]; } return Matrix(slices); } public: this(size_t height, size_t width) { writeln(__FUNCTION__); rows = new int[][](height, width); } this(int[][] rows) { writeln(__FUNCTION__); this.rows = rows; } void toString(void delegate(const(char)[]) sink) const { sink.formattedWrite!"%(%(%5s %)\n%)"(rows); } /* 指定した値を、行列の各要素に * 割り当てる。 */ Matrix opAssign(int value) { writeln(__FUNCTION__); foreach (row; rows) { row[] = value; } return this; } /* 各要素と値を2進演算で使用し、 * その結果をその要素に割り当てる。 */ Matrix opOpAssign(string op)(int value) { writeln(__FUNCTION__); foreach (row; rows) { mixin ("row[] " ~ op ~ "= value;"); } return this; } /* 指定した次元の長さを返す。 */ size_t opDollar(size_t dimension)() const if (dimension <= 1) { writeln(__FUNCTION__); static if (dimension == 0) { /* 次元0の長さは、 * 'rows'配列の長さである。 */ return rows.length; } else { /* 次元1の長さは、'rows'の * 要素の長さだ。 */ return rows.length ? rows[0].length : 0; } } /* 'begin'から'end'までの範囲を表す * オブジェクトを返す。 * * 注釈: テンプレートパラメータ'dimension'は * ここでは使用されていないが、その情報は他の型で * 役立つ場合がある。 */ Range opSlice(size_t dimension)(size_t begin, size_t end) if (dimension <= 1) { writeln(__FUNCTION__); return Range(begin, end); } /* 引数で定義されたサブマトリックスを * 返す。 */ Matrix opIndex(A...)(A arguments) if (A.length <= 2) { writeln(__FUNCTION__); /* まず、マトリックス全体を表す範囲から始める。 * これにより、パラメーターなしのopIndexは * "すべての要素"を意味する。 */ Range[2] ranges = [ Range(0, opDollar!0), Range(0, opDollar!1) ]; foreach (dimension, a; arguments) { static if (is (typeof(a) == Range)) { /* この次元は既に'matrix[begin..end]'のような * 範囲として指定されており、 * そのまま使用できる。 */ ranges[dimension] = a; } else static if (is (typeof(a) : size_t)) { /* この次元は、'matrix[i]'のような * 単一のインデックス値として指定されており、 * 単一の要素の範囲として表現したい。 */ ranges[dimension] = Range(a, a + 1); } else { /* 他の型は想定していない。 */ static assert( false, format("Invalid index type: %s", typeof(a).stringof)); } } /* 'arguments'で指定された部分行列を * 返す。 */ return subMatrix(ranges[0], ranges[1]); } /* 指定された値を部分行列の各要素に * 割り当てる。 */ Matrix opIndexAssign(A...)(int value, A arguments) if (A.length <= 2) { writeln(__FUNCTION__); Matrix subMatrix = opIndex(arguments); return subMatrix = value; } /* サブ行列の各要素と値を * 二項演算で使用し、その結果をその * 要素に代入する。 */ Matrix opIndexOpAssign(string op, A...)(int value, A arguments) if (A.length <= 2) { writeln(__FUNCTION__); Matrix subMatrix = opIndex(arguments); mixin ("return subMatrix " ~ op ~ "= value;"); } } /* 文字列として指定された式を実行し、 * 結果と行列の新しい状態を * 表示する。 */ void execute(string expression)(Matrix m) { writefln("\n--- %s ---", expression); mixin ("auto result = " ~ expression ~ ";"); writefln("result:\n%s", result); writefln("m:\n%s", m); } void main() { enum height = 10; enum width = 8; auto m = Matrix(height, width); int counter = 0; foreach (row; 0 .. height) { foreach (column; 0 .. width) { writefln("Initializing %s of %s", counter + 1, height * width); m[row, column] = counter; ++counter; } } writeln(m); execute!("m[1, 1] = 42")(m); execute!("m[0, 1 .. $] = 43")(m); execute!("m[0 .. $, 3] = 44")(m); execute!("m[$-4 .. $-1, $-4 .. $-1] = 7")(m); execute!("m[1, 1] *= 2")(m); execute!("m[0, 1 .. $] *= 4")(m); execute!("m[0 .. $, 0] *= 10")(m); execute!("m[$-4 .. $-2, $-4 .. $-2] -= 666")(m); execute!("m[1, 1]")(m); execute!("m[2, 0 .. $]")(m); execute!("m[0 .. $, 2]")(m); execute!("m[0 .. $ / 2, 0 .. $ / 2]")(m); execute!("++m[1..3, 1..3]")(m); execute!("--m[2..5, 2..5]")(m); execute!("m[]")(m); execute!("m[] = 20")(m); execute!("m[] /= 4")(m); execute!("(m[] += 5) /= 10")(m); }