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 }