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 <vladimir@thecybershadow.net> 22 */ 23 24 module ae.utils.meta.tuplerange; 25 26 /// 27 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 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 auto trOnly(T...)(T values) /// ditto 88 { 89 static struct Result 90 { 91 T values; this(T values) { this.values = values; } 92 93 bool opCall(Next)(Next next) 94 { 95 foreach (ref value; values) 96 if (next(value)) 97 return true; 98 return false; 99 } 100 } 101 return Result(values); 102 } 103 104 unittest 105 { 106 static int fun() 107 { 108 int a = 2; 109 int offset = 1; 110 int x; 111 trOnly(0, 1., 2f) 112 .trMap!(n => n + offset) 113 .trFilter!(n => n > a) 114 .trEach!((n) { x = cast(int)n; } ); 115 return x; 116 } 117 static assert(fun() == 3); 118 } 119 120 unittest 121 { 122 static int fun() 123 { 124 int a = 2; 125 int offset = 1; 126 int result; 127 int x = 0; double y = 1.; float z = 2f; 128 trOnly(x, y, z) 129 .trMap!(n => n + offset) 130 .trFilter!(n => n > a) 131 .trEach!((n) { result = cast(int)n; } ); 132 return result; 133 } 134 static assert(fun() == 3); 135 } 136 137 unittest 138 { 139 struct S 140 { 141 int a = 1; 142 long b = 2; 143 ubyte c = 3; 144 } 145 S s; 146 147 int[] results; 148 s.tupleof 149 .trOnly 150 .trEach!((long n) { results ~= cast(int)n; }); 151 assert(results == [1, 2, 3]); 152 } 153 154 /// Passes only values satisfying the given predicate to the next 155 /// layer. 156 auto trFilter(alias pred, R)(auto ref R r) 157 { 158 struct Result 159 { 160 R r; this(R r) { this.r = r; } 161 162 bool opCall(Next)(Next next) 163 { 164 struct Handler 165 { 166 bool opCall(T)(auto ref T value) 167 { 168 if (!pred(value)) 169 return false; // keep going 170 return next(value); 171 } 172 } 173 Handler handler; 174 return r(handler); 175 } 176 } 177 return Result(r); 178 } 179 180 /// 181 unittest 182 { 183 int a = 2; 184 int b = 3; 185 int[] results; 186 foreach (i; 0..10) 187 (i) 188 .trOnly 189 .trFilter!(n => n % a == 0) 190 .trFilter!(n => n % b == 0) 191 .trEach!((int n) { results ~= n; }); 192 assert(results == [0, 6]); 193 } 194 195 /// Like trFilter, but evaluates pred at compile-time with each 196 /// element's type. 197 auto trCTFilter(alias pred, R)(auto ref R r) 198 { 199 struct Result 200 { 201 R r; this(R r) { this.r = r; } 202 203 bool opCall(Next)(Next next) 204 { 205 struct Handler 206 { 207 bool opCall(T)(auto ref T value) 208 { 209 static if (!pred!T) 210 return false; // keep going 211 else 212 return next(value); 213 } 214 } 215 Handler handler; 216 return r(handler); 217 } 218 } 219 return Result(r); 220 } 221 222 /// 223 unittest 224 { 225 enum isNumeric(T) = is(typeof(cast(int)T.init)); 226 int[] results; 227 trOnly(1, 2., "3", '\x04') 228 .trCTFilter!isNumeric 229 .trEach!((n) { results ~= cast(int)n; }); 230 assert(results == [1, 2, 4]); 231 } 232 233 /// Transforms values using the given predicate before passing them to 234 /// the next layer. 235 auto trMap(alias pred, R)(auto ref R r) 236 { 237 struct Result 238 { 239 R r; this(R r) { this.r = r; } 240 241 bool opCall(Next)(Next next) 242 { 243 struct Handler 244 { 245 bool opCall(T)(auto ref T value) 246 { 247 return next(pred(value)); 248 } 249 } 250 Handler handler; 251 return r(handler); 252 } 253 } 254 return Result(r); 255 } 256 257 /// 258 unittest 259 { 260 int result; 261 (2) 262 .trOnly 263 .trMap!(n => n+1) 264 .trMap!(n => n * 2) 265 .trEach!((int n) { result = n; }); 266 assert(result == 6); 267 } 268 269 /// Sink, calls predicate over each value in r. 270 /// If predicate returns a boolean, use that to determine whether to 271 /// stop or keep going. 272 auto trEach(alias pred, R)(auto ref R r) 273 { 274 struct Handler 275 { 276 bool opCall(T)(auto ref T value) 277 { 278 alias R = typeof(pred(value)); 279 static if (is(R == bool)) 280 return pred(value); 281 else 282 static if (is(R == void)) 283 { 284 pred(value); 285 return false; // keep going 286 } 287 else 288 static assert(false); 289 } 290 } 291 Handler handler; 292 return r(handler); 293 } 294 295 /// Calls predicate with only the first value in r. 296 auto trFront(alias pred, R)(auto ref R r) 297 { 298 struct Handler 299 { 300 bool opCall(T)(auto ref T value) 301 { 302 pred(value); 303 return true; 304 } 305 } 306 Handler handler; 307 return r(handler); 308 } 309 310 /// r is a tuple range of tuple ranges. 311 /// Process it as one big range. 312 auto trJoiner(R)(auto ref R r) 313 { 314 struct Result 315 { 316 R r; this(R r) { this.r = r; } 317 318 bool opCall(Next)(Next next) 319 { 320 struct Handler 321 { 322 bool opCall(T)(auto ref T value) 323 { 324 return value(next); 325 } 326 } 327 Handler handler; 328 return r(handler); 329 } 330 } 331 return Result(r); 332 } 333 334 unittest 335 { 336 int[] values; 337 trOnly( 338 trOnly(1, 2f), 339 trOnly(3.0, '\x04'), 340 ) 341 .trJoiner 342 .trEach!((n) { values ~= cast(int)n; }); 343 assert(values == [1, 2, 3, 4]); 344 } 345 346 /// Convert a regular (homogeneous) range to a tuple range. 347 auto trIter(R)(auto ref R r) 348 { 349 struct Result 350 { 351 R r; this(R r) { this.r = r; } 352 353 bool opCall(Next)(Next next) 354 { 355 foreach (ref e; r) 356 if (next(e)) 357 return true; 358 return false; 359 } 360 } 361 return Result(r); 362 } 363 364 unittest 365 { 366 int[] values; 367 trOnly( 368 [1., 2.].trIter, 369 ['\x03', '\x04'].trIter, 370 ) 371 .trJoiner 372 .trEach!((n) { values ~= cast(int)n; }); 373 assert(values == [1, 2, 3, 4]); 374 375 }