1 // Written in the D programming language. 2 /* 3 NYSL Version 0.9982 4 5 A. This software is "Everyone'sWare". It means: 6 Anybody who has this software can use it as if he/she is 7 the author. 8 9 A-1. Freeware. No fee is required. 10 A-2. You can freely redistribute this software. 11 A-3. You can freely modify this software. And the source 12 may be used in any software with no limitation. 13 A-4. When you release a modified version to public, you 14 must publish it with your name. 15 16 B. The author is not responsible for any kind of damages or loss 17 while using or misusing this software, which is distributed 18 "AS IS". No warranty of any kind is expressed or implied. 19 You use AT YOUR OWN RISK. 20 21 C. Copyrighted to Kazuki KOMATSU 22 23 D. Above three clauses are applied both to source and binary 24 form of this software. 25 */ 26 27 /** 28 このモジュールは、標準ライブラリのstd.functionalを強化します。 29 */ 30 31 module carbon.functional; 32 33 import std.algorithm, 34 std.array, 35 std.format, 36 std.range, 37 std.string, 38 std.traits, 39 std.typecons; 40 41 /** 42 このテンプレートは、$(M std.functional.unaryFun)や$(M std.functional.binaryFun)を一般化し、 43 N個の引数を取れるようにしたものです。 44 $(M unaryFun)や$(M binaryFun)のように文字列に対して作用する場合は、その文字列で表される関数となります。 45 対して、文字列以外を$(M naryFun)に適用した場合には、その対象へのaliasとなります。 46 47 文字列による形式には、現在のところアルファベット及び数字に対応しています。 48 */ 49 template naryFun(alias fun, int N = -1) 50 if(is(typeof(fun) == string)) 51 { 52 auto ref naryFunAlphabet(T...)(auto ref T args) 53 { 54 static assert(T.length <= 26); 55 mixin(createAliasAlphabet(T.length)); 56 return mixin(fun); 57 } 58 59 60 auto ref naryFunNumber(T...)(auto ref T args) 61 { 62 mixin(createAliasNumber(T.length)); 63 return mixin(fun); 64 } 65 66 67 auto ref naryFun(T...)(auto ref T args) 68 if(!(N >= 0) || T.length == N) 69 { 70 static if(is(typeof({naryFunNumber(forward!args);}))) 71 return naryFunNumber(forward!args); 72 else 73 return naryFunAlphabet(forward!args); 74 } 75 76 77 string createAliasAlphabet(size_t nparam) 78 { 79 auto app = appender!string(); 80 foreach(i; 0 .. nparam) 81 app.formattedWrite("alias %s = args[%s];\n", cast(char)(i + 'a'), i); 82 return app.data; 83 } 84 85 86 string createAliasNumber(size_t nparam) 87 { 88 auto app = appender!string(); 89 foreach(i; 0 .. nparam) 90 app.formattedWrite("alias _%1$s = args[%1$s];\n", i); 91 return app.data; 92 } 93 } 94 95 /// ditto 96 template naryFun(alias fun, int N = -1) 97 if(!is(typeof(fun) == string)) 98 { 99 auto ref naryFun(T...)(auto ref T args) 100 if(!(N >= 0) || T.length == N) 101 { 102 return fun(forward!args); 103 } 104 } 105 106 /// 107 unittest 108 { 109 alias test1 = naryFun!"a"; 110 assert(test1(1) == 1); 111 assert(test1(test1(2.0)) == 2.0); 112 static assert(is(typeof({test1(1, 1);}))); // OK 113 // 最初の引数を返す関数だから。 114 // 2つ目の引数は使用されない。 115 116 static assert(!is(typeof({test1();}))); // NG 117 118 119 alias test1_1 = naryFun!("a", 1); // 引数の数を1つとする 120 assert(test1_1(1) == 1); 121 assert(test1_1(test1(2.0)) == 2.0); 122 static assert(!is(typeof({test1_1(1, 1);}))); // NG 123 124 125 alias test1_2 = naryFun!("a", 2); // 引数の数を2つとする 126 assert(test1_2(1, 1) == 1); 127 assert(test1_2(test1_2(2.0, 2), 1) == 2.0); 128 static assert(!is(typeof({test1_2(1);}))); // NG 129 130 131 alias test2 = naryFun!"b"; 132 assert(test2(1, 2) == 2); 133 assert(test2(test2(1, "2"), test2(3.0, '4')) == '4'); 134 static assert(!is(typeof({test2();}))); 135 static assert(!is(typeof({test2(1);}))); 136 static assert(is(typeof({test2(1, 1, 2.2);}))); 137 138 139 // アルファベット 140 alias test3 = naryFun!"a + b + c"; 141 assert(test3(1, 2, 3) == 6); 142 143 import std.bigint; 144 assert(test3(BigInt(1), 2, 3) == BigInt(6)); 145 146 147 // 数字 148 alias test4 = naryFun!"_0 + _1 + _2 + _3"; 149 assert(test4(1, 2, 3, 4) == 10); 150 } 151 152 153 /** 154 ある関数funcの引数に、タプルを適用できるようにします。 155 */ 156 template adaptTuple(alias func, T...) 157 { 158 auto _toRvalue(X)(ref X a) 159 { 160 return a; 161 } 162 163 164 string _toRvalueNargs(size_t N) 165 { 166 return format("return func(%(_toRvalue(arg.field[%s])%|,%));", iota(N)); 167 } 168 169 170 static if(T.length > 0) 171 { 172 auto ref adaptTuple(X)(ref X arg) 173 if(is(Unqual!X : Tuple!U, U...) && is(typeof({Tuple!T a = Unqual!X.init;}))) 174 { 175 return func(arg.field); 176 } 177 178 auto ref adaptTuple(X)(X arg) 179 if(is(Unqual!X : Tuple!U, U...) && is(typeof({Tuple!T a = Unqual!X.init;}))) 180 { 181 mixin(_toRvalueNargs(T.length)); 182 } 183 } 184 else 185 { 186 auto ref adaptTuple(X)(ref X arg) 187 if(is(Unqual!X == Tuple!U, U...)) 188 { 189 return func(arg.field); 190 } 191 192 193 auto ref adaptTuple(X)(X arg) 194 if(is(Unqual!X == Tuple!U, U...)) 195 { 196 mixin(_toRvalueNargs(X.field.length)); 197 } 198 } 199 } 200 201 /// 202 unittest 203 { 204 static ref int func1(ref int a, float b, byte c) 205 { 206 return a; 207 } 208 209 alias adpt1 = adaptTuple!func1; 210 auto t = tuple(4, 1.1f, cast(byte)2); 211 assert(&adpt1(t) == &(t.field[0])); 212 213 // NG; adpt1の引数がlvalueじゃない(std.forwardのような転送) 214 static assert(!is(typeof({adpt1(tuple(4, 1.1f, cast(byte)2));}))); 215 216 // naryFun & 事前に型指定あり 217 alias adpt2 = adaptTuple!(naryFun!"a", int, float, byte); 218 assert(&adpt2(t) == &(t.field[0])); // forward性 219 assert(adpt2(tuple(4, 1.1f, cast(byte)2)) == t.field[0]); 220 221 // NG; stringはfloatへ暗黙変換不可能 222 static assert(!is(typeof({adpt2(tuple(1, "foo", cast(byte)2));}))); 223 224 // OK; realはfloatへ暗黙変換可能 225 static assert(is(typeof({adpt2(tuple(1, 1.1L, cast(byte)2));}))); 226 assert(adpt2(tuple(1, 1.1L, cast(byte)2)) == 1); 227 228 const ct = t; 229 static assert(is(typeof(adpt2(ct)) == const)); 230 231 immutable it = t; 232 static assert(is(typeof(adpt2(it)) == immutable)); 233 234 shared st = t; 235 static assert(is(typeof(adpt2(st)) == shared)); 236 237 238 assert(adaptTuple!(naryFun!"a")(tuple(1, 2, 3)) == 1); 239 assert(adaptTuple!(naryFun!"a", int)(tuple(1)) == 1); 240 } 241