1 /**
2 * Tuple ranges
3 *
4 * Contains constructs for iterating and chaining together operations
5 * on range-like constructs which operate on heterogeneous types.
6 *
7 * To allow heterogeneous elements, iteration is internal rather than
8 * external. The range elements are functors - calling a range with
9 * a function parameter "next" asks the range to iterate over its
10 * members and call "next" over each one. "next" returns a bool
11 * (true if iteration should stop, false if it should continue),
12 * which is propagated by the range's opCall upwards.
13 *
14 * License:
15 * This Source Code Form is subject to the terms of
16 * the Mozilla Public License, v. 2.0. If a copy of
17 * the MPL was not distributed with this file, You
18 * can obtain one at http://mozilla.org/MPL/2.0/.
19 *
20 * Authors:
21 * Vladimir Panteleev <ae@cy.md>
22 */
23
24 module ae.utils.meta.tuplerange;
25
26 ///
27 debug(ae_unittest) unittest
28 {
29 int a = 2;
30 int offset = 1;
31 int x;
32 trOnly(0, 1., 2f)
33 .trMap!(n => n + offset)
34 .trFilter!(n => n > a)
35 .trEach!((n) { x = cast(int)n; } );
36 assert(x == 3);
37 }
38
39 import std.meta;
40
41 import ae.utils.meta.caps;
42
43 debug(ae_unittest) unittest
44 {
45 static struct X
46 {
47 bool fun(int x)
48 {
49 return this.tupleof
50 .trOnly
51 .trEach!(n => n == x);
52 }
53
54 long a;
55 int b;
56 ubyte c;
57 }
58
59 X x;
60 assert(!x.fun(5));
61 x.c = 5;
62 assert(x.fun(5));
63 }
64
65 /// Source, iterates over the given values.
66 auto trOnly(T...)(ref return T values)
67 {
68 alias PointerTo(T) = T*;
69 alias P = staticMap!(PointerTo, T);
70 struct Result
71 {
72 P values; this(P values) { this.values = values; }
73 bool opCall(Next)(Next next)
74 {
75 foreach (ref value; values)
76 if (next(*value))
77 return true;
78 return false;
79 }
80 }
81 P pvalues;
82 foreach (i, ref value; values)
83 pvalues[i] = &value;
84 return Result(pvalues);
85 }
86
87 /// ditto
88 auto trOnly(T...)(T values)
89 {
90 static struct Result
91 {
92 T values; this(T values) { this.values = values; }
93
94 bool opCall(Next)(Next next)
95 {
96 foreach (ref value; values)
97 if (next(value))
98 return true;
99 return false;
100 }
101 }
102 return Result(values);
103 }
104
105 debug(ae_unittest) unittest
106 {
107 static int fun()
108 {
109 int a = 2;
110 int offset = 1;
111 int x;
112 trOnly(0, 1., 2f)
113 .trMap!(n => n + offset)
114 .trFilter!(n => n > a)
115 .trEach!((n) { x = cast(int)n; } );
116 return x;
117 }
118 static assert(fun() == 3);
119 }
120
121 debug(ae_unittest) unittest
122 {
123 static int fun()
124 {
125 int a = 2;
126 int offset = 1;
127 int result;
128 int x = 0; double y = 1.; float z = 2f;
129 trOnly(x, y, z)
130 .trMap!(n => n + offset)
131 .trFilter!(n => n > a)
132 .trEach!((n) { result = cast(int)n; } );
133 return result;
134 }
135 static assert(fun() == 3);
136 }
137
138 debug(ae_unittest) unittest
139 {
140 struct S
141 {
142 int a = 1;
143 long b = 2;
144 ubyte c = 3;
145 }
146 S s;
147
148 int[] results;
149 s.tupleof
150 .trOnly
151 .trEach!((long n) { results ~= cast(int)n; });
152 assert(results == [1, 2, 3]);
153 }
154
155 /// Passes only values satisfying the given predicate to the next
156 /// layer.
157 auto trFilter(alias pred, R)(auto ref R r)
158 {
159 struct Result
160 {
161 R r; this(R r) { this.r = r; }
162
163 bool opCall(Next)(Next next)
164 {
165 struct Handler
166 {
167 bool opCall(T)(auto ref T value)
168 {
169 if (!pred(value))
170 return false; // keep going
171 return next(value);
172 }
173 }
174 Handler handler;
175 return r(handler);
176 }
177 }
178 return Result(r);
179 }
180
181 ///
182 debug(ae_unittest) unittest
183 {
184 int a = 2;
185 int b = 3;
186 int[] results;
187 foreach (i; 0..10)
188 (i)
189 .trOnly
190 .trFilter!(n => n % a == 0)
191 .trFilter!(n => n % b == 0)
192 .trEach!((int n) { results ~= n; });
193 assert(results == [0, 6]);
194 }
195
196 /// Like trFilter, but evaluates pred at compile-time with each
197 /// element's type.
198 auto trCTFilter(alias pred, R)(auto ref R r)
199 {
200 struct Result
201 {
202 R r; this(R r) { this.r = r; }
203
204 bool opCall(Next)(Next next)
205 {
206 struct Handler
207 {
208 bool opCall(T)(auto ref T value)
209 {
210 static if (!pred!T)
211 return false; // keep going
212 else
213 return next(value);
214 }
215 }
216 Handler handler;
217 return r(handler);
218 }
219 }
220 return Result(r);
221 }
222
223 ///
224 debug(ae_unittest) unittest
225 {
226 enum isNumeric(T) = is(typeof(cast(int)T.init));
227 int[] results;
228 trOnly(1, 2., "3", '\x04')
229 .trCTFilter!isNumeric
230 .trEach!((n) { results ~= cast(int)n; });
231 assert(results == [1, 2, 4]);
232 }
233
234 /// Transforms values using the given predicate before passing them to
235 /// the next layer.
236 auto trMap(alias pred, R)(auto ref R r)
237 {
238 struct Result
239 {
240 R r; this(R r) { this.r = r; }
241
242 bool opCall(Next)(Next next)
243 {
244 struct Handler
245 {
246 bool opCall(T)(auto ref T value)
247 {
248 return next(pred(value));
249 }
250 }
251 Handler handler;
252 return r(handler);
253 }
254 }
255 return Result(r);
256 }
257
258 ///
259 debug(ae_unittest) unittest
260 {
261 int result;
262 (2)
263 .trOnly
264 .trMap!(n => n+1)
265 .trMap!(n => n * 2)
266 .trEach!((int n) { result = n; });
267 assert(result == 6);
268 }
269
270 /// Sink, calls predicate over each value in r.
271 /// If predicate returns a boolean, use that to determine whether to
272 /// stop or keep going.
273 auto trEach(alias pred, R)(auto ref R r)
274 {
275 struct Handler
276 {
277 bool opCall(T)(auto ref T value)
278 {
279 alias R = typeof(pred(value));
280 static if (is(R == bool))
281 return pred(value);
282 else
283 static if (is(R == void))
284 {
285 pred(value);
286 return false; // keep going
287 }
288 else
289 static assert(false);
290 }
291 }
292 Handler handler;
293 return r(handler);
294 }
295
296 /// Calls predicate with only the first value in r.
297 auto trFront(alias pred, R)(auto ref R r)
298 {
299 struct Handler
300 {
301 bool opCall(T)(auto ref T value)
302 {
303 pred(value);
304 return true;
305 }
306 }
307 Handler handler;
308 return r(handler);
309 }
310
311 /// r is a tuple range of tuple ranges.
312 /// Process it as one big range.
313 auto trJoiner(R)(auto ref R r)
314 {
315 struct Result
316 {
317 R r; this(R r) { this.r = r; }
318
319 bool opCall(Next)(Next next)
320 {
321 struct Handler
322 {
323 bool opCall(T)(auto ref T value)
324 {
325 return value(next);
326 }
327 }
328 Handler handler;
329 return r(handler);
330 }
331 }
332 return Result(r);
333 }
334
335 debug(ae_unittest) unittest
336 {
337 int[] values;
338 trOnly(
339 trOnly(1, 2f),
340 trOnly(3.0, '\x04'),
341 )
342 .trJoiner
343 .trEach!((n) { values ~= cast(int)n; });
344 assert(values == [1, 2, 3, 4]);
345 }
346
347 /// Convert a regular (homogeneous) range to a tuple range.
348 auto trIter(R)(auto ref R r)
349 {
350 struct Result
351 {
352 R r; this(R r) { this.r = r; }
353
354 bool opCall(Next)(Next next)
355 {
356 foreach (ref e; r)
357 if (next(e))
358 return true;
359 return false;
360 }
361 }
362 return Result(r);
363 }
364
365 debug(ae_unittest) unittest
366 {
367 int[] values;
368 trOnly(
369 [1., 2.].trIter,
370 ['\x03', '\x04'].trIter,
371 )
372 .trJoiner
373 .trEach!((n) { values ~= cast(int)n; });
374 assert(values == [1, 2, 3, 4]);
375
376 }