1 /**
2  * std.algorithm-like functions which accept functors as predicates.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 module ae.utils.functor.algorithm;
15 
16 import std.range.primitives : isInputRange;
17 import std.traits : Unqual;
18 
19 import std.range; // array range primitives
20 
21 import ae.utils.functor.primitives;
22 
23 /// `std.algorithm.map` variant which accepts a functor predicate.
24 auto map(Range, P)(Range r, P pred)
25 if (isInputRange!(Unqual!Range))
26 {
27 	return PMapResult!(Range, P)(r, pred);
28 }
29 
30 private struct PMapResult(R, P)
31 {
32 	bool empty() { return r.empty; }
33 	auto front() { return pred(r.front); }
34 	static if (__traits(hasMember, R, "back"))
35 		auto back() { return pred(r.back); }
36 	void popFront() { r.popFront; }
37 
38 private:
39 	R r;
40 	P pred;
41 }
42 
43 ///
44 @nogc unittest
45 {
46 	import std.algorithm.comparison : equal;
47 	import std.range : iota, only;
48 	import std.typecons : tuple;
49 
50 	// Simple map. Delegates are functors too!
51 	assert(5.iota.map((int n) => n + 1).equal(only(1, 2, 3, 4, 5)));
52 
53 	// Now with an explicit functor object (no indirect call):
54 	assert(5.iota.map(functor!((int n) => n + 1)).equal(only(1, 2, 3, 4, 5)));
55 
56 	// With state (in @nogc !!!)
57 	int addend = 1;
58 	assert(5.iota.map(functor!((addend, n) => n + addend)(addend)).equal(only(1, 2, 3, 4, 5)));
59 
60 	// Aggregate state with tuples:
61 	auto p = functor!((state, n) => (n + state.addend) * state.factor)(
62 		tuple!("addend", "factor")(1, 2)
63 	);
64 	assert(5.iota.map(p).equal(only(2, 4, 6, 8, 10)));
65 
66 	// ... or just pass multiple parameters:
67 	auto q = functor!((addend, factor, n) => (n + addend) * factor)(1, 2);
68 	assert(5.iota.map(q).equal(only(2, 4, 6, 8, 10)));
69 }
70 
71 /// `std.algorithm.filter` variant which accepts a functor predicate.
72 auto filter(Range, P)(Range r, P pred)
73 if (isInputRange!(Unqual!Range))
74 {
75 	return PFilterResult!(Range, P)(r, pred);
76 }
77 
78 private struct PFilterResult(R, P)
79 {
80 	bool empty() { return r.empty; }
81 	auto front() { return pred(r.front); }
82 	void popFront() { r.popFront(); advance(); }
83 
84 	this(R r, P pred)
85 	{
86 		this.r = r;
87 		this.pred = pred;
88 		advance();
89 	}
90 
91 private:
92 	R r;
93 	P pred;
94 
95 	void advance()
96 	{
97 		while (!r.empty && !pred(r.front))
98 			r.popFront();
99 	}
100 }