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 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 /// 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 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 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 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 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 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 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 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 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 }