1 // SemiTwist Library
2 // Written in the D programming language.
3 
4 module semitwist.util.functional;
5 
6 /+version(Unittest)+/ import std.stdio;
7 
8 import semitwist.util.all;
9 
10 
11 //TODO: Think about new naming scheme. Take a look at how tango does it.
12 template makeDg2To1(string str, T)
13 {
14 	enum makeDg2To1 = "("~T.stringof~" a, "~T.stringof~" b){ return ("~str~"); }";
15 	//pragma(msg, "makeDg2To1: "~makeDg2To1);
16 }
17 template makeDg2To1(string str, T1, T2)
18 {
19 	enum makeDg2To1 = "("~T1.stringof~" a, "~T2.stringof~" b){ return ("~str~"); }";
20 	//pragma(msg, "makeDg2To1: "~makeDg2To1);
21 }
22 
23 template makeDg1To1(string str, T)
24 {
25 	enum makeDg1To1 = "("~T.stringof~" a){ return ("~str~"); }";
26 	//pragma(msg, "makeDg1To1: "~makeDg1To1);
27 }
28 
29 T reduce(T)(T[] list, T delegate(T a, T b) dg)
30 {
31 	return list.length==0? T.init  :
32 	       list.length==1? list[0] :
33 	                       list[1..$].reduce(list[0], dg);
34 }
35 
36 T reduce(T)(T[] list, T init, T delegate(T a, T b) dg)
37 {
38 	T result = init;
39 	foreach(T elem; list)
40 		result = dg(result, elem);
41 	
42 	return result;
43 }
44 
45 TOut reduceTo(TOut, TIn)(TIn[] list, TOut delegate(TOut a, TIn b) dg)
46 {
47 	return list.length==0? TOut.init :
48 	                       list.reduceTo(TOut.init, dg);
49 }
50 
51 TOut reduceTo(TOut, TIn)(TIn[] list, TOut init, TOut delegate(TOut a, TIn b) dg)
52 {
53 	TOut result = init;
54 	foreach(TIn elem; list)
55 		result = dg(result, elem);
56 	
57 	return result;
58 }
59 
60 T reduce(string dgstr, T)(T[] list)
61 {
62 	return reduce(list, mixin(makeDg2To1!(dgstr, T)));
63 }
64 
65 T reduce(string dgstr, T)(T[] list, T init)
66 {
67 	return reduce(list, init, mixin(makeDg2To1!(dgstr, T)));
68 }
69 
70 TOut reduceTo(TOut, string dgstr, TIn)(TIn[] list)
71 {
72 	return reduceTo(list, mixin(makeDg2To1!(dgstr, TOut, TIn)));
73 }
74 
75 TOut reduceTo(TOut, string dgstr, TIn)(TIn[] list, TOut init)
76 {
77 	return reduceTo(list, init, mixin(makeDg2To1!(dgstr, TOut, TIn)));
78 }
79 
80 TOut[] map(TOut, TIn)(TIn[] list, TOut delegate(TIn a) dg)
81 {
82 	TOut[] result;
83 	result.length = list.length;
84 	foreach(size_t i, TIn elem; list)
85 		result[i] = dg(elem);
86 	
87 	return result;
88 }
89 
90 TOut[TKey] map(TOut, TIn, TKey)(TIn[TKey] list, TOut delegate(TIn a, TKey b) dg)
91 {
92 	TOut[TKey] result;
93 	foreach(TKey key, TIn elem; list)
94 		result[key] = dg(elem, key);
95 	
96 	return result;
97 }
98 
99 T[] mapAAtoA(T, TKey)(T[TKey] list, T delegate(T a, TKey b) dg)
100 {
101 	return mapAAtoATo(list, dg);
102 }
103 
104 TOut[] mapAAtoATo(TOut, TIn, TKey)(TIn[TKey] list, TOut delegate(TIn a, TKey b) dg)
105 {
106 	size_t i=0;
107 	TOut[] result;
108 	result.length = list.length;
109 	foreach(TKey key, TIn elem; list)
110 	{
111 		result[i] = dg(elem, key);
112 		i++;
113 	}
114 	
115 	return result;
116 }
117 
118 T[] map(string dgstr, T)(T[] list)
119 {
120 	return map(list, mixin(makeDg1To1!(dgstr, T)));
121 }
122 
123 T[TKey] map(string dgstr, T, TKey)(T[TKey] list)
124 {
125 	return map(list, mixin(makeDg2To1!(dgstr, T, TKey)));
126 }
127 
128 TOut[] mapTo(TOut, string dgstr, TIn)(TIn[] list)
129 {
130 	return map(list, mixin(makeDg1To1!(dgstr, TIn)));
131 }
132 
133 TOut[TKey] mapTo(TOut, string dgstr, TIn, TKey)(TIn[TKey] list)
134 {
135 	return map(list, mixin(makeDg2To1!(dgstr, TIn, TKey)));
136 }
137 
138 T[] mapAAtoA(string dgstr, T, TKey)(T[TKey] list)
139 {
140 	return mapAAtoA(list, mixin(makeDg2To1!(dgstr, T, TKey)));
141 }
142 
143 TOut[] mapAAtoATo(TOut, string dgstr, TIn, TKey)(TIn[TKey] list)
144 {
145 	return mapAAtoATo(list, mixin(makeDg2To1!(dgstr, TIn, TKey)));
146 }
147 
148 /+T[] filter(T)(T[] list, bool delegate(T a) dg)
149 {
150 	T[] result = list.dup;
151 	auto numRemaining = result.removeIf((T a){return !dg(a);});
152 	return result[0..numRemaining];
153 }
154 
155 T[] filter(string dgstr, T)(T[] list)
156 {
157 	return filter(list, mixin(makeDg1To1!(dgstr, T)));
158 }+/
159 
160 //TODO: Make foreachWhile
161 //TODO: Make variant that also provides an index to the delegate
162 //TODO: Make variant for AA
163 /// Like foreach, except the body has a return value,
164 /// and the loop bails whenever that value != whileVal
165 TRet foreachWhileVal(TRet, TElem)(TElem[] coll, TRet whileVal, TRet delegate(TElem) dg)
166 {
167 	foreach(TElem elem; coll)
168 	{
169 		auto ret = dg(elem);
170 		if(ret != whileVal)
171 			return ret;
172 	}
173 	return whileVal;
174 }
175 
176 mixin(unittestSemiTwistDLib(q{
177 	int[string] aa = ["a":1, "b":2, "c":3];
178 	int[string] expected;
179 	int[string] result;
180 
181 	mixin(deferEnsure!(`aa.keys`,   `_ == ["a","b","c"]`));
182 
183 	// Map
184 	int[] array = [1, 2, 3, 4, 5];
185 	mixin(deferEnsure!(`map(array, (int a){return a*10;})`, `_ == [10,20,30,40,50]`));
186 	mixin(deferEnsure!(`map!("a*10")(array)`, `_ == [10,20,30,40,50]`));
187 
188 	// Map assoc array using dg literal
189 	result = map(aa, (int a, string b){return a*10;});
190 	// Workaround for DMD Bug #1671
191 	//mixin(deferEnsure!(`result`, `_ == ['a':10,'b':20,'c':30]`));
192 	mixin(deferEnsure!(`result.length`, `_ == 3`));
193 	mixin(deferEnsure!(`result.keys`,   `_ == ["a","b","c"]`));
194 	//mixin(traceVal!("result.keys", "result.keys.length"));
195 	//mixin(traceVal!("result.keys[0]", "result.keys[1]", `result.keys[2]~""`));
196 	mixin(deferEnsure!(`result.values`, `_ == [10,20,30]`));
197 
198 	// Map assoc array using dg string
199 	result = map!(`a*10`)(aa);
200 	mixin(deferEnsure!(`result.length`, `_ == 3`));
201 	mixin(deferEnsure!(`result.keys`,   `_ == ["a","b","c"]`));
202 	mixin(deferEnsure!(`result.values`, `_ == [10,20,30]`));
203 
204 	// mapAAtoA
205 	mixin(deferEnsure!(`mapAAtoA(aa, (int a, string b){return a*10;})`, `_ == [10,20,30]`));
206 	mixin(deferEnsure!(`mapAAtoA!("a*10")(aa)`, `_ == [10,20,30]`));
207 
208 	// mapAAtoA To
209 	mixin(deferEnsure!(`mapAAtoATo(aa, (int a, string b){return a*10+0.5;})`, `_ == [10.5,20.5,30.5]`));
210 	mixin(deferEnsure!(`mapAAtoATo!(double, "a*10+0.5")(aa)`, `_ == [10.5,20.5,30.5]`));
211 		
212 	// Reduce
213 	mixin(deferEnsure!(`reduce(array, (int a, int b){return a*b;})`, `_ == (1*2*3*4*5)`));
214 	mixin(deferEnsure!(`reduce!("a*b")(array)`, `_ == (1*2*3*4*5)`));
215 	
216 	// Reduce To
217 	mixin(deferEnsure!(`reduceTo(array, (int[] a, int b){return a~(b*10);})`, `_ == [10,20,30,40,50]`));
218 	mixin(deferEnsure!(`reduceTo!(int[], "a~(b*10)")(array)`, `_ == [10,20,30,40,50]`));
219 	
220 	// Reduce using initial value
221 	mixin(deferEnsure!(`reduce(array, 10, (int a, int b){return a*b;})`, `_ == (10*1*2*3*4*5)`));
222 	mixin(deferEnsure!(`reduce!("a*b")(array, 10)`, `_ == (10*1*2*3*4*5)`));
223 	
224 	// Reduce empty array
225 	mixin(deferEnsure!(`reduce(cast(int[])[], (int a, int b){return a*b;})`, `_ == 0`));
226 	mixin(deferEnsure!(`reduce!("a*b")(cast(int[])[])`, `_ == 0`));
227 
228 	//TODO: Reduce assoc array
229 	
230 	// Filter
231 /+	mixin(deferEnsure!(`filter(array, (int a){return (a%2)==0;})`, `_ == [2,4]`));
232 	mixin(deferEnsure!(`filter!("(a%2)==0")(array)`, `_ == [2,4]`));
233 +/
234 	//TODO: Filter assoc array
235 	
236 }));