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