Changeset 1714
- Timestamp:
- 07/04/10 21:04:48 (14 years ago)
- Files:
-
- trunk/phobos/std/algorithm.d (modified) (11 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/phobos/std/algorithm.d
r1712 r1714 46 46 module std.algorithm; 47 47 48 48 import std.c.string; 49 49 import std.array, std.container, std.contracts, std.conv, std.date, 50 50 std.functional, std.math, std.metastrings, std.range, std.string, 51 51 std.traits, std.typecons, std.typetuple; 52 52 53 53 version(unittest) 54 54 { 55 55 import std.random, std.stdio, std.string; 56 57 56 mixin(dummyRanges); 58 57 } 59 58 60 59 /** 61 60 Implements the homonym function (also known as $(D transform)) present 62 61 in many languages of functional flavor. The call $(D map!(fun)(range)) 63 62 returns a range of which elements are obtained by applying $(D fun(x)) 64 63 left to right for all $(D x) in $(D range). The original ranges are 65 64 not changed. Evaluation is done lazily. The range returned by $(D map) 66 65 caches the last value such that evaluating $(D front) multiple times … … 111 110 } 112 111 } 113 112 } 114 113 115 114 struct Map(alias fun, Range) if (isInputRange!(Range)) 116 115 { 117 116 alias typeof(fun(.ElementType!(Range).init)) ElementType; 118 117 Range _input; 119 118 ElementType _cache; 120 119 121 static if (isBidirectionalRange!(Range)) {120 static if (isBidirectionalRange!(Range)) { 122 121 // Using a second cache would lead to at least 1 extra function evaluation 123 122 // and wasted space when 99% of the time this range will only be iterated 124 123 // over in the forward direction. Use a bool to determine whether cache 125 124 // is front or back instead. 126 bool cacheIsBack ;125 bool cacheIsBack_; 127 126 128 127 private void fillCacheBack() { 129 128 if (!_input.empty) _cache = fun(_input.back); 130 cacheIsBack = true;129 cacheIsBack_ = true; 131 130 } 132 131 133 132 @property ElementType back() { 134 if (!cacheIsBack) {133 if (!cacheIsBack_) { 135 134 fillCacheBack(); 136 135 } 137 136 return _cache; 138 137 } 139 138 140 139 void popBack() { 141 140 _input.popBack; 142 141 fillCacheBack(); 143 142 } 144 143 } 145 144 146 145 private void fillCache() { 147 146 if (!_input.empty) _cache = fun(_input.front); 148 147 149 148 static if(isBidirectionalRange!(Range)) { 150 cacheIsBack = false;149 cacheIsBack_ = false; 151 150 } 152 151 } 153 152 154 153 this(Range input) { 155 154 _input = input; 156 155 fillCache; 157 156 } 158 157 159 static if (isInfinite!Range) {158 static if (isInfinite!Range) { 160 159 // Propagate infinite-ness. 161 160 enum bool empty = false; 162 161 } else { 163 162 @property bool empty() { 164 163 return _input.empty; 165 164 } 166 165 } 167 166 168 167 void popFront() { 169 168 _input.popFront; 170 fillCache ;169 fillCache(); 171 170 } 172 171 173 172 @property ElementType front() { 174 static if (isBidirectionalRange!(Range)) {175 if (cacheIsBack) {173 static if (isBidirectionalRange!(Range)) { 174 if (cacheIsBack_) { 176 175 fillCache(); 177 176 } 178 177 } 179 178 return _cache; 180 179 } 181 180 182 static if (isRandomAccessRange!Range) {181 static if (isRandomAccessRange!Range) { 183 182 ElementType opIndex(size_t index) { 184 183 return fun(_input[index]); 185 184 } 186 185 } 187 186 188 187 // hasLength is busted, Bug 2873 189 static if (is(typeof(_input.length) : size_t)188 static if (is(typeof(_input.length) : size_t) 190 189 || is(typeof(_input.length()) : size_t)) { 191 190 @property size_t length() { 192 191 return _input.length; 193 192 } 194 193 } 195 194 196 static if (hasSlicing!(Range)) {195 static if (hasSlicing!(Range)) { 197 196 typeof(this) opSlice(size_t lowerBound, size_t upperBound) { 198 197 return typeof(this)(_input[lowerBound..upperBound]); 199 198 } 200 199 } 201 200 202 201 static if (isForwardRange!Range) 203 202 @property Map save() 204 203 { 205 Map result; 206 result._input = _input.save; 207 result._cache = _cache; 208 return result; 204 return this; 209 205 } 210 206 } 211 207 212 208 unittest 213 209 { 214 210 scope(failure) writeln("Unittest failed at line ", __LINE__); 215 211 int[] arr1 = [ 1, 2, 3, 4 ]; 216 212 int[] arr2 = [ 5, 6 ]; 217 213 auto squares = map!("a * a")(arr1); 218 214 assert(equal(squares, [ 1, 4, 9, 16 ][])); 219 215 assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][])); 220 216 221 217 // Test the caching stuff. 222 auto squares2 = squares; 218 assert(squares.back == 16); 219 auto squares2 = squares.save; 223 220 assert(squares2.back == 16); 221 224 222 assert(squares2.front == 1); 225 223 squares2.popFront; 226 224 assert(squares2.front == 4); 227 225 squares2.popBack; 228 226 assert(squares2.front == 4); 229 227 assert(squares2.back == 9); 230 228 231 229 assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][])); 232 230 233 231 uint i; … … 571 569 while (!_input.empty && !pred(_input.back)) _input.popBack; 572 570 } 573 571 574 572 } 575 573 576 574 ref Filter opSlice() 577 575 { 578 576 return this; 579 577 } 580 578 581 static if (isInfinite!Range) {579 static if (isInfinite!Range) { 582 580 enum bool empty = false; // Propagate infiniteness. 583 581 } else { 584 582 bool empty() { return _input.empty; } 585 583 } 586 584 587 585 void popFront() 588 586 { 589 587 do 590 588 { 591 589 _input.popFront; … … 603 601 do 604 602 { 605 603 _input.popBack; 606 604 } while (!_input.empty && !pred(_input.back)); 607 605 } 608 606 609 607 ElementType!(Range) back() { return _input.back;} 610 608 } 611 609 612 610 613 static if (isForwardRange!Range)611 static if (isForwardRange!Range) 614 612 { 615 613 @property typeof(this) save() 616 614 { 617 615 return typeof(this)(_input.save); 618 616 } 619 617 } 620 618 } 621 619 622 620 unittest 623 621 { … … 638 636 auto nums = [0,1,2,3,4]; 639 637 auto forward = filter!"a % 2 == 0"(nums); 640 638 assert(equal(retro(forward), [4,2,0][])); // f is a bidirectional range 641 639 642 640 643 641 foreach(DummyType; AllDummyRanges) { 644 642 DummyType d; 645 643 auto f = filter!"a & 1"(d); 646 644 assert(equal(f, [1,3,5,7,9])); 647 645 648 static if (isForwardRange!DummyType) {646 static if (isForwardRange!DummyType) { 649 647 static assert(isForwardRange!(typeof(f))); 650 648 } 651 649 652 static if (isBidirectionalRange!DummyType) {650 static if (isBidirectionalRange!DummyType) { 653 651 static assert(isBidirectionalRange!(typeof(f))); 654 652 assert(equal(retro(f), [9,7,5,3,1])); 655 653 } 656 654 } 657 655 } 658 656 659 657 // move 660 658 /** 661 659 Moves $(D source) into $(D target) via a destructive 662 660 copy. Specifically: $(UL $(LI If $(D hasAliasing!T) is true (see … … 869 867 870 868 public: 871 869 this(Range input, Separator separator) 872 870 { 873 871 _input = input; 874 872 _separator = separator; 875 873 // computeFront(); 876 874 // computeBack(); 877 875 } 878 876 879 static if (isInfinite!Range)877 static if (isInfinite!Range) 880 878 { 881 879 enum bool empty = false; 882 880 } else 883 881 { 884 882 @property bool empty() 885 883 { 886 884 return _frontLength == _atEnd; 887 885 } 888 886 } 889 887 … … 1004 1002 _separator = separator; 1005 1003 } 1006 1004 1007 1005 @property Range front() 1008 1006 { 1009 1007 assert(!empty); 1010 1008 ensureFrontLength; 1011 1009 return _input[0 .. _frontLength]; 1012 1010 } 1013 1011 1014 static if (isInfinite!Range)1012 static if (isInfinite!Range) 1015 1013 { 1016 1014 enum bool empty = false; // Propagate infiniteness 1017 1015 } 1018 1016 else 1019 1017 { 1020 1018 @property bool empty() 1021 1019 { 1022 1020 return _frontLength == size_t.max && _input.empty; 1023 1021 } 1024 1022 } … … 1151 1149 else 1152 1150 { 1153 1151 // Chase first terminator 1154 1152 while (_end < _input.length && !_isTerminator(_input[_end])) 1155 1153 { 1156 1154 ++_end; 1157 1155 } 1158 1156 } 1159 1157 } 1160 1158 1161 static if (isInfinite!Range)1159 static if (isInfinite!Range) 1162 1160 { 1163 1161 enum bool empty = false; // Propagate infiniteness. 1164 1162 } 1165 1163 else 1166 1164 { 1167 1165 @property bool empty() 1168 1166 { 1169 1167 return _end == _end.max; 1170 1168 } 1171 1169 } … … 1274 1272 auto last = _input.front; 1275 1273 do 1276 1274 { 1277 1275 _input.popFront; 1278 1276 } 1279 1277 while (!_input.empty && binaryFun!(pred)(last, _input.front)); 1280 1278 } 1281 1279 1282 1280 @property ElementType!(R) front() { return _input.front; } 1283 1281 1284 static if (isBidirectionalRange!R)1282 static if (isBidirectionalRange!R) 1285 1283 { 1286 1284 void popBack() 1287 1285 { 1288 1286 auto last = _input.back; 1289 1287 do 1290 1288 { 1291 1289 _input.popBack; 1292 1290 } 1293 1291 while (!_input.empty && binaryFun!(pred)(last, _input.back)); 1294 1292 } 1295 1293 1296 1294 @property ElementType!(R) back() { return _input.back; } 1297 1295 } 1298 1296 1299 static if (isInfinite!R)1297 static if (isInfinite!R) 1300 1298 { 1301 1299 enum bool empty = false; // Propagate infiniteness. 1302 1300 } 1303 1301 else 1304 1302 { 1305 1303 @property bool empty() { return _input.empty; } 1306 1304 } 1307 1305 1308 1306 1309 static if (isForwardRange!R) {1307 static if (isForwardRange!R) { 1310 1308 @property typeof(this) save() { 1311 1309 return typeof(this)(_input.save); 1312 1310 } 1313 1311 } 1314 1312 } 1315 1313 1316 1314 /// Ditto 1317 1315 Uniq!(pred, Range) uniq(alias pred = "a == b", Range)(Range r) 1318 1316 { 1319 1317 return typeof(return)(r); … … 1326 1324 assert(equal(r, [ 1, 2, 3, 4, 5 ][])); 1327 1325 assert(equal(retro(r), retro([ 1, 2, 3, 4, 5 ][]))); 1328 1326 1329 1327 foreach(DummyType; AllDummyRanges) { 1330 1328 DummyType d; 1331 1329 auto u = uniq(d); 1332 1330 assert(equal(u, [1,2,3,4,5,6,7,8,9,10])); 1333 1331 1334 1332 static assert(d.rt == RangeType.Input || isForwardRange!(typeof(u))); 1335 1333 1336 static if (d.rt >= RangeType.Bidirectional) {1334 static if (d.rt >= RangeType.Bidirectional) { 1337 1335 assert(equal(retro(u), [10,9,8,7,6,5,4,3,2,1])); 1338 1336 } 1339 1337 } 1340 1338 } 1341 1339 1342 1340 // group 1343 1341 /** 1344 1342 Similarly to $(D uniq), $(D group) iterates unique consecutive 1345 1343 elements of the given range. The element type is $(D 1346 1344 Tuple!(ElementType!R, uint)) because it includes the count of … … 1380 1378 _current = tuple(_input.front, 1u); 1381 1379 _input.popFront; 1382 1380 while (!_input.empty && comp(_current.field[0], _input.front)) 1383 1381 { 1384 1382 ++_current.field[1]; 1385 1383 _input.popFront; 1386 1384 } 1387 1385 } 1388 1386 } 1389 1387 1390 static if (isInfinite!R)1388 static if (isInfinite!R) 1391 1389 { 1392 1390 enum bool empty = false; // Propagate infiniteness. 1393 1391 } 1394 1392 else 1395 1393 { 1396 1394 @property bool empty() 1397 1395 { 1398 1396 return _current.field[1] == 0; 1399 1397 } 1400 1398 } 1401 1399 1402 1400 @property ref Tuple!(ElementType!R, uint) front() 1403 1401 { 1404 1402 assert(!empty); 1405 1403 return _current; 1406 1404 } 1407 1405 1408 static if (isForwardRange!R) {1406 static if (isForwardRange!R) { 1409 1407 @property typeof(this) save() { 1410 1408 typeof(this) ret; 1411 1409 ret._input = this._input.save; 1412 1410 ret._current = this._current; 1413 1411 1414 1412 return ret; 1415 1413 } 1416 1414 } 1417 1415 } 1418 1416
