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.functional, 37 std.range, 38 std..string, 39 std.traits, 40 std.typecons; 41 42 /** 43 このテンプレートは、$(M std.functional.unaryFun)や$(M std.functional.binaryFun)を一般化し、 44 N個の引数を取れるようにしたものです。 45 $(M unaryFun)や$(M binaryFun)のように文字列に対して作用する場合は、その文字列で表される関数となります。 46 対して、文字列以外を$(M naryFun)に適用した場合には、その対象へのaliasとなります。 47 48 文字列による形式には、現在のところアルファベット及び数字に対応しています。 49 */ 50 template naryFun(alias fun, int N = -1) 51 if(is(typeof(fun) == string)) 52 { 53 auto ref naryFunAlphabet(T...)(auto ref T args) 54 { 55 static assert(T.length <= 26); 56 mixin(createAliasAlphabet(T.length)); 57 return mixin(fun); 58 } 59 60 61 auto ref naryFunNumber(T...)(auto ref T args) 62 { 63 mixin(createAliasNumber(T.length)); 64 return mixin(fun); 65 } 66 67 68 auto ref naryFun(T...)(auto ref T args) 69 if(!(N >= 0) || T.length == N) 70 { 71 static if(is(typeof({naryFunNumber(forward!args);}))) 72 return naryFunNumber(forward!args); 73 else 74 return naryFunAlphabet(forward!args); 75 } 76 77 78 string createAliasAlphabet(size_t nparam) 79 { 80 auto app = appender!string(); 81 foreach(i; 0 .. nparam) 82 app.formattedWrite("alias %s = args[%s];\n", cast(char)(i + 'a'), i); 83 return app.data; 84 } 85 86 87 string createAliasNumber(size_t nparam) 88 { 89 auto app = appender!string(); 90 foreach(i; 0 .. nparam) 91 app.formattedWrite("alias _%1$s = args[%1$s];\n", i); 92 return app.data; 93 } 94 } 95 96 /// ditto 97 template naryFun(alias fun, int N = -1) 98 if(!is(typeof(fun) == string)) 99 { 100 auto ref naryFun(T...)(auto ref T args) 101 if(!(N >= 0) || T.length == N) 102 { 103 return fun(forward!args); 104 } 105 } 106 107 /// 108 unittest 109 { 110 alias test1 = naryFun!"a"; 111 assert(test1(1) == 1); 112 assert(test1(test1(2.0)) == 2.0); 113 static assert(is(typeof({test1(1, 1);}))); // OK 114 // 最初の引数を返す関数だから。 115 // 2つ目の引数は使用されない。 116 117 static assert(!is(typeof({test1();}))); // NG 118 119 120 alias test1_1 = naryFun!("a", 1); // 引数の数を1つとする 121 assert(test1_1(1) == 1); 122 assert(test1_1(test1(2.0)) == 2.0); 123 static assert(!is(typeof({test1_1(1, 1);}))); // NG 124 125 126 alias test1_2 = naryFun!("a", 2); // 引数の数を2つとする 127 assert(test1_2(1, 1) == 1); 128 assert(test1_2(test1_2(2.0, 2), 1) == 2.0); 129 static assert(!is(typeof({test1_2(1);}))); // NG 130 131 132 alias test2 = naryFun!"b"; 133 assert(test2(1, 2) == 2); 134 assert(test2(test2(1, "2"), test2(3.0, '4')) == '4'); 135 static assert(!is(typeof({test2();}))); 136 static assert(!is(typeof({test2(1);}))); 137 static assert(is(typeof({test2(1, 1, 2.2);}))); 138 139 140 // アルファベット 141 alias test3 = naryFun!"a + b + c"; 142 assert(test3(1, 2, 3) == 6); 143 144 import std.bigint; 145 assert(test3(BigInt(1), 2, 3) == BigInt(6)); 146 147 148 // 数字 149 alias test4 = naryFun!"_0 + _1 + _2 + _3"; 150 assert(test4(1, 2, 3, 4) == 10); 151 } 152 153 154 /** 155 ある関数funcの引数に、タプルを適用できるようにします。 156 */ 157 template adaptTuple(alias func, T...) 158 { 159 auto _toRvalue(X)(ref X a) 160 { 161 return a; 162 } 163 164 165 string _toRvalueNargs(size_t N) 166 { 167 return format("return func(%(_toRvalue(arg.field[%s])%|,%));", iota(N)); 168 } 169 170 171 static if(T.length > 0) 172 { 173 auto ref adaptTuple(X)(ref X arg) 174 if(is(Unqual!X : Tuple!U, U...) && is(typeof({Tuple!T a = Unqual!X.init;}))) 175 { 176 return func(arg.field); 177 } 178 179 auto ref adaptTuple(X)(X arg) 180 if(is(Unqual!X : Tuple!U, U...) && is(typeof({Tuple!T a = Unqual!X.init;}))) 181 { 182 mixin(_toRvalueNargs(T.length)); 183 } 184 } 185 else 186 { 187 auto ref adaptTuple(X)(ref X arg) 188 if(is(Unqual!X == Tuple!U, U...)) 189 { 190 return func(arg.field); 191 } 192 193 194 auto ref adaptTuple(X)(X arg) 195 if(is(Unqual!X == Tuple!U, U...)) 196 { 197 mixin(_toRvalueNargs(X.field.length)); 198 } 199 } 200 } 201 202 /// 203 unittest 204 { 205 static ref int func1(ref int a, float b, byte c) 206 { 207 return a; 208 } 209 210 alias adpt1 = adaptTuple!func1; 211 auto t = tuple(4, 1.1f, cast(byte)2); 212 assert(&adpt1(t) == &(t.field[0])); 213 214 // NG; adpt1の引数がlvalueじゃない(std.forwardのような転送) 215 static assert(!is(typeof({adpt1(tuple(4, 1.1f, cast(byte)2));}))); 216 217 // naryFun & 事前に型指定あり 218 alias adpt2 = adaptTuple!(naryFun!"a", int, float, byte); 219 assert(&adpt2(t) == &(t.field[0])); // forward性 220 assert(adpt2(tuple(4, 1.1f, cast(byte)2)) == t.field[0]); 221 222 // NG; stringはfloatへ暗黙変換不可能 223 static assert(!is(typeof({adpt2(tuple(1, "foo", cast(byte)2));}))); 224 225 // OK; realはfloatへ暗黙変換可能 226 static assert(is(typeof({adpt2(tuple(1, 1.1L, cast(byte)2));}))); 227 assert(adpt2(tuple(1, 1.1L, cast(byte)2)) == 1); 228 229 const ct = t; 230 static assert(is(typeof(adpt2(ct)) == const)); 231 232 immutable it = t; 233 static assert(is(typeof(adpt2(it)) == immutable)); 234 235 shared st = t; 236 static assert(is(typeof(adpt2(st)) == shared)); 237 238 239 assert(adaptTuple!(naryFun!"a")(tuple(1, 2, 3)) == 1); 240 assert(adaptTuple!(naryFun!"a", int)(tuple(1)) == 1); 241 } 242 243 244 /** 245 関数を信頼関数にします 246 */ 247 auto ref assumeTrusted(alias fn, T...)(auto ref T args) @trusted 248 { 249 return naryFun!fn(forward!args); 250 } 251 252 253 /** 254 255 */ 256 auto ref passTo(alias f, T...)(auto ref T args) 257 { 258 f(forward!args); 259 return args[0]; 260 }