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 }));