1 //Written in the D programming language 2 /* 3 * Some algorithm routines and templates 4 * 5 * Copyright 2013-6 Jaypha 6 * 7 * Distributed under the Boost Software License, Version 1.0. 8 * (See http://www.boost.org/LICENSE_1_0.txt) 9 * 10 * Authors: Jason den Dulk 11 */ 12 13 14 module jaypha.algorithm; 15 16 import jaypha.traits; 17 18 import std.range.primitives; 19 20 public import std.algorithm; 21 import std.typecons, std.range, std.array, std.traits; 22 23 24 //---------------------------------------------------------------------------- 25 // Consumes the front of the range as long the elements are inside pattern. 26 27 auto munch(alias pred = "a == b", R1,R2)(ref R1 range, R2 pattern) 28 if (isInputRange!R1 && isInputRange!R2 && 29 isScalarType!(ElementType!R2) && isScalarType!(ElementType!R1)) 30 { 31 alias ElementType!R1 E1; 32 alias ElementType!R2 E2; 33 34 auto a = appender!(E1[]); 35 36 for (; !range.empty && !find!pred(pattern, cast(E2)range.front).empty; range.popFront()) 37 a.put(range.front); 38 39 return a.data; 40 } 41 42 unittest 43 { 44 import std.array; 45 import std.range.interfaces; 46 47 ubyte[] txt = cast(ubyte[]) "acabbacbxyz".dup; 48 49 txt.munch("abc"); 50 assert(txt == "xyz"); 51 } 52 53 //----------------------------------------------------------------------------- 54 // Grabs as much of seq, unitl a character is found that matches choices 55 // according to the given predicate. Returns the first part while setting 56 // seq to the remainder (including the found character). 57 58 auto grab(alias pred = "a == b", R1, R2)(ref R1 seq, R2 choices) 59 if (!hasSlicing!R1 && isInputRange!R1 && isForwardRange!R2) 60 { 61 alias ElementType!R1 E; 62 63 auto a = appender!(E[]); 64 65 for (; !seq.empty && find!pred(choices, seq.front).empty; seq.popFront()) 66 a.put(seq.front); 67 return a.data; 68 } 69 70 71 S1 grab(alias pred = "a == b",S1, S2)(ref S1 seq, S2 choices) 72 if (hasSlicing!S1 && isForwardRange!S2) 73 { 74 auto remainder = findAmong!(pred,S1,S2)(seq, choices); 75 scope(exit) seq = remainder; 76 return seq[0..$-remainder.length]; 77 } 78 79 //------------------------------------- 80 81 unittest 82 { 83 dstring s = "abcdefg"; 84 dstring c = "cg"; 85 86 auto x = grab(s,c); 87 assert(x == "ab"); 88 assert(s.front == 'c'); 89 s = s[1..$]; 90 x = grab(s,c); 91 assert(x == "def"); 92 assert(s.front == 'g'); 93 94 string t1 = "to be or not to be"; 95 auto t = grab(t1, "abc"); 96 assert(t == "to "); 97 assert(t1 == "be or not to be"); 98 } 99 100 //---------------------------------------------------------------------------- 101 // Map-like algorithm that merges the index and values of an associative 102 // array. 103 104 template meld(alias fun) 105 { 106 auto meld(A)(A t) if (isAssociativeArray!A) 107 { 108 return MeldResult!(fun, A)(t); 109 } 110 } 111 112 private struct MeldResult(alias F, T:T[U],U) 113 { 114 U[] val; 115 T[U] home; 116 117 this(T[U] t) { home = t; val = t.keys; } 118 119 @property bool empty() { return (val.length == 0); } 120 @property U front() { return F(val[0],home[val[0]]); } 121 void popFront() { val = val[1..$]; } 122 } 123 124 //------------------------------------- 125 126 unittest 127 { 128 string[string] x = [ "one":"1", "bee":"3", "john":"66" ]; 129 130 auto y = x.meld!((a,b) => (a~b)); 131 string[] yy = new string[](3); 132 y.copy(yy); 133 sort(yy); // Do sort to make the order predictable 134 assert(yy == ["bee3","john66","one1"]); 135 } 136 137 //---------------------------------------------------------------------------- 138 // Returns everything in primary that is not in secondary 139 140 T[] diff(T)(T[] primary, T[] secondary) 141 { 142 auto result = appender!(T[])(); 143 result.reserve(primary.length); 144 145 foreach (t;primary) 146 { 147 if (!secondary.canFind(t)) 148 result.put(t); 149 } 150 151 return result.data; 152 } 153 154 //------------------------------------- 155 156 unittest 157 { 158 auto t1 = [ 2, 12, 5, 25, 5, 7 ]; 159 auto t2 = [ 2, 3, 17, 5 ]; 160 161 auto t3 = diff(t1,t2); 162 163 assert(t3 == [ 12, 25, 7 ]); 164 } 165 166 //---------------------------------------------------------------------------- 167 // Alternative to std.alogrithm.findSplit usable with non-rewindable ranges. 168 169 auto findSplit(R1,R2)(ref R1 haystack, R2 needle) 170 if (isInputRange!R1 && isDynamicArray!R2 && isComparable!(ElementType!R1,ElementType!R2)) 171 { 172 import std.algorithm.searching : endsWith, findSplitBefore; 173 174 assert(needle.length > 0); 175 auto store = appender!R2(); 176 while (!haystack.empty) 177 { 178 store.put(haystack.front); 179 haystack.popFront(); 180 if (store.data.endsWith(needle)) 181 return store.data.findSplitBefore(needle); 182 } 183 return tuple(store.data, uninitializedArray!(R2)(0)); 184 } 185 186 //------------------------------------- 187 188 unittest 189 { 190 import std.stdio; 191 string haystack = "donôbôbôbcbxyz"; 192 string needle = "ôbôbc"; 193 194 auto res = findSplit(haystack, needle); 195 assert(res[0] == "donôb"); 196 assert(res[1] == "ôbôbc"); 197 assert(haystack == "bxyz"); 198 199 auto res2 = findSplit(haystack, needle); 200 assert(res2[0] == "bxyz"); 201 assert(res2[1] == ""); 202 assert(haystack == ""); 203 204 haystack = "asdfjkewu"; 205 auto res3 = findSplit(haystack, "a"); 206 assert(res3[0] == ""); 207 assert(res3[1] == "a"); 208 assert(haystack == "sdfjkewu"); 209 210 haystack = ""; 211 auto res4 = findSplit(haystack, "p"); 212 assert(res4[0] == ""); 213 assert(res4[1] == ""); 214 assert(haystack == ""); 215 216 struct NoForward 217 { 218 ubyte[] bytes; 219 @property bool empty() { return bytes.length == 0; } 220 @property ubyte front() { return bytes[0]; } 221 void popFront() { bytes = bytes[1..$]; } 222 } 223 224 auto x = NoForward(cast(ubyte[])"123456789"); 225 auto n = cast(ubyte[]) "456"; 226 auto res5 = findSplit(x,n); 227 assert(res5[0] == cast(ubyte[]) "123"); 228 assert(res5[1] == cast(ubyte[]) "456"); 229 230 res5 = findSplit(x,n); 231 assert(res5[0] == cast(ubyte[]) "789"); 232 assert(res5[1] == cast(ubyte[]) ""); 233 234 } 235 236 //---------------------------------------------------------------------------- 237 // Similar to std.algorithm.map except that the mapping function is not a 238 // template parameter 239 240 struct rtMap(R,T) if(isInputRange!R) 241 { 242 this(R range, T delegate(ElementType!R) mapper) 243 { 244 _range = range; 245 _mapper = mapper; 246 if (!_range.empty) 247 _front = _mapper(_range.front); 248 } 249 250 @property T front() { return _front; } 251 @property bool empty() { return _range.empty; } 252 void popFront() 253 { 254 _range.popFront(); 255 if (!_range.empty) 256 _front = _mapper(_range.front); 257 } 258 259 private: 260 R _range; 261 T _front; 262 T delegate(ElementType!R) _mapper; 263 } 264 265 unittest 266 { 267 uint m1(uint i) { return i+3; } 268 uint m2(uint i) { return i+6; } 269 270 uint[] src = [ 0, 4, 7 ]; 271 272 auto rmap = rtMap!(uint[], uint)(src, &m1); 273 274 auto dest = appender!(uint[]); 275 rmap.copy(dest); 276 277 assert(dest.data == [ 3, 7, 10 ]); 278 279 rmap = rtMap!(uint[], uint)(src, &m2); 280 281 dest = appender!(uint[]); 282 rmap.copy(dest); 283 284 assert(dest.data == [ 6, 10, 13 ]); 285 } 286 287 //---------------------------------------------------------------------------- 288