Changeset 1691
- Timestamp:
- 06/24/10 00:38:40 (14 years ago)
- Files:
-
- trunk/phobos/std/range.d (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/phobos/std/range.d
r1690 r1691 24 24 import std.contracts; 25 25 import std.traits; 26 26 import std.typecons; 27 27 import std.typetuple; 28 28 import std.algorithm; 29 29 import std.functional; 30 30 import std.conv; 31 31 version(unittest) 32 32 { 33 33 import std.container, std.conv, std.math, std.stdio; 34 35 // Used with the dummy ranges for testing higher order ranges. 36 enum RangeType { 37 Input, 38 Forward, 39 Bidirectional, 40 Random 41 } 42 43 enum Length { 44 Yes, 45 No 46 } 47 48 enum ReturnBy { 49 Reference, 50 Value 51 } 52 53 // Range that's useful for testing other higher order ranges, 54 // can be parametrized with attributes. It just dumbs down an array of 55 // numbers 1..10. 56 struct DummyRange(ReturnBy _r, Length _l, RangeType _rt) { 57 // These enums are so that the template params are visible outside 58 // this instantiation. 59 enum r = _r; 60 enum l = _l; 61 enum rt = _rt; 62 63 uint[] arr = [1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]; 64 65 void reinit() { 66 // Workaround for DMD bug 4378 67 arr = [1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]; 68 } 69 70 void popFront() { 71 arr = arr[1..$]; 72 } 73 74 @property bool empty() { 75 return arr.length == 0; 76 } 77 78 static if(r == ReturnBy.Reference) { 79 @property ref uint front() { 80 return arr[0]; 81 } 82 } else { 83 @property uint front() { 84 return arr[0]; 85 } 86 } 87 88 static if(rt >= RangeType.Forward) { 89 @property typeof(this) save() { 90 return this; 91 } 92 } 93 94 static if(rt >= RangeType.Bidirectional) { 95 void popBack() { 96 arr = arr[0..$ - 1]; 97 } 98 99 static if(r == ReturnBy.Reference) { 100 @property ref uint back() { 101 return arr[$ - 1]; 102 } 103 } else { 104 @property uint back() { 105 return arr[$ - 1]; 106 } 107 } 108 } 109 110 static if(rt >= RangeType.Random) { 111 static if(r == ReturnBy.Reference) { 112 @property ref uint opIndex(size_t index) { 113 return arr[index]; 114 } 115 } else { 116 @property uint opIndex(size_t index) { 117 return arr[index]; 118 } 119 } 120 } 121 122 static if(l == Length.Yes) { 123 @property size_t length() { 124 return arr.length; 125 } 126 } 127 } 128 129 alias TypeTuple!( 130 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input), 131 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), 132 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), 133 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), 134 DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input), 135 DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), 136 DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional), 137 DummyRange!(ReturnBy.Reference, Length.No, RangeType.Random), 138 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input), 139 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward), 140 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional), 141 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random), 142 DummyRange!(ReturnBy.Value, Length.No, RangeType.Input), 143 DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward), 144 DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional), 145 DummyRange!(ReturnBy.Value, Length.No, RangeType.Random) 146 ) AllDummyRanges; 147 148 alias TypeTuple!(1,2,3,4,5,6,7,8,9,0,11,12,13,14,15,16) DummyIndices; 34 149 } 35 150 36 151 /** 37 152 Returns $(D true) if $(D R) is an input range. An input range must 38 153 define the primitives $(D empty), $(D popFront), and $(D front). The 39 154 following code should compile for any input range. 40 155 41 156 ---- 42 157 R r; // can define a range object 43 158 if (r.empty) {} // can test for empty … … 619 734 assert(r.front == witness.front); 620 735 assert(r.back == witness.back); 621 736 assert(equal(r, witness)); 622 737 } 623 738 test([ 1 ], [ 1 ]); 624 739 test([ 1, 2 ], [ 2, 1 ]); 625 740 test([ 1, 2, 3 ], [ 3, 2, 1 ]); 626 741 test([ 1, 2, 3, 4 ], [ 4, 3, 2, 1 ]); 627 742 test([ 1, 2, 3, 4, 5 ], [ 5, 4, 3, 2, 1 ]); 628 743 test([ 1, 2, 3, 4, 5, 6 ], [ 6, 5, 4, 3, 2, 1 ]); 744 745 foreach(DummyType; AllDummyRanges) { 746 static if(!isBidirectionalRange!DummyType) { 747 static assert(!__traits(compiles, Retro!DummyType)); 748 } else { 749 DummyType dummyRange; 750 dummyRange.reinit(); 751 752 auto myRetro = retro(dummyRange); 753 assert(myRetro.front == 10); 754 assert(myRetro.back == 1); 755 756 static if(isRandomAccessRange!DummyType && hasLength!DummyType) { 757 assert(myRetro[0] == myRetro.front); 758 759 static if(DummyType.r == ReturnBy.Reference) { 760 myRetro[9]++; 761 assert(dummyRange[0] == 2); 762 myRetro.front++; 763 assert(myRetro.front == 11); 764 myRetro.back++; 765 assert(myRetro.back == 3); 766 } 767 } 768 } 769 } 629 770 } 630 771 631 772 /** 632 773 Iterates range $(D r) with stride $(D n). If the range is a 633 774 random-access range, moves by indexing into the range; otehrwise, 634 775 moves by successive calls to $(D popFront). 635 776 636 777 Example: 637 778 ---- 638 779 int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]; … … 655 796 _n = n; 656 797 static if (hasLength!(R)) 657 798 { 658 799 auto slack = _input.length % _n; 659 800 if (slack) slack--; 660 801 if (!slack) return; 661 802 static if (isRandomAccessRange!(R) && hasSlicing!(R)) 662 803 { 663 804 _input = _input[0 .. _input.length - slack]; 664 805 } 665 else 806 else static if(isBidirectionalRange!(R)) 666 807 { 667 808 foreach (i; 0 .. slack) 668 809 { 669 810 if (_input.empty) break; 670 811 _input.popBack; 671 812 } 672 813 } 673 814 } 674 815 } 675 816 676 817 /** 677 818 Returns $(D this). 678 819 */ 679 @property Stride save() 680 { 681 return Stride(_input.save, _n); 820 static if(isForwardRange!(R)) 821 { 822 @property Stride save() 823 { 824 return Stride(_input.save, _n); 825 } 682 826 } 683 827 684 828 /** 685 829 Forwards to $(D _input.empty). 686 830 */ 687 831 bool empty() 688 832 { 689 833 return _input.empty; 690 834 } 691 835 … … 704 848 foreach (i; 0 .. _n) 705 849 { 706 850 _input.popFront; 707 851 if (_input.empty) break; 708 852 } 709 853 } 710 854 711 855 /** 712 856 Forwards to $(D _input.popFront). 713 857 */ 714 static if ( hasLength!(R))858 static if (isBidirectionalRange!(R) && hasLength!(R)) 715 859 void popBack() 716 860 { 717 861 enforce(_input.length >= _n); 718 862 static if (isRandomAccessRange!(R) && hasSlicing!(R)) 719 863 { 720 864 _input = _input[0 .. _input.length - _n]; 721 865 } 722 866 else 723 867 { 724 868 foreach (i; 0 .. _n) … … 733 877 Forwards to $(D _input.front). 734 878 */ 735 879 ref ElementType!(R) front() 736 880 { 737 881 return _input.front; 738 882 } 739 883 740 884 /** 741 885 Forwards to $(D _input.back) after getting rid of any slack items. 742 886 */ 743 ref ElementType!(R) back() 744 { 745 return _input.back; 887 static if(isBidirectionalRange!(R) && hasLength!(R)) 888 { 889 ref ElementType!(R) back() 890 { 891 return _input.back; 892 } 746 893 } 747 894 748 895 /** 749 896 Forwards to $(D _input[_input.length - n + 1]). Defined only if $(D R) 750 897 is a random access range and if $(D R) defines $(D R.length). 751 898 */ 752 899 static if (isRandomAccessRange!(R) && hasLength!(R)) 753 900 ref ElementType!(R) opIndex(uint n) 754 901 { 755 902 return _input[_n * n]; … … 781 928 void test(size_t n, int[] input, int[] witness) 782 929 { 783 930 assert(equal(stride(input, n), witness)); 784 931 } 785 932 test(1, [], []); 786 933 int[] arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 787 934 test(1, arr, arr); 788 935 test(2, arr, [1, 3, 5, 7, 9]); 789 936 test(3, arr, [1, 4, 7, 10]); 790 937 test(4, arr, [1, 5, 9]); 938 939 foreach(DummyType; AllDummyRanges) { 940 static if(DummyType.r == ReturnBy.Reference) { 941 // Doesn't work yet w/o ref returns, see DMD bug 3294. 942 DummyType dummyRange; 943 dummyRange.reinit(); 944 945 auto myStride = stride(dummyRange, 4); 946 assert(myStride.front == 1); 947 assert(equal(myStride, [1, 5, 9])); 948 949 static if(hasLength!DummyType) { 950 assert(myStride.length == 3); 951 } 952 953 static if(isBidirectionalRange!DummyType && hasLength!DummyType) { 954 assert(myStride.back == 9); 955 } 956 957 static if(isRandomAccessRange!DummyType && hasLength!DummyType) { 958 assert(myStride[0] == 1); 959 assert(myStride[1] == 5); 960 assert(myStride[2] == 9); 961 } 962 } 963 } 791 964 } 792 965 793 966 /** 794 967 Spans multiple ranges in sequence. The function $(D chain) takes any 795 968 number of ranges and returns a $(D Chain!(R1, R2,...)) object. The 796 969 ranges may be different, but they must have the same element type. The 797 970 result is a range that offers the $(D front), $(D popFront), and $(D empty) 798 971 primitives. If all input ranges offer random access and $(D length), 799 972 $(D Chain) offers them as well. 800 973 … … 1631 1804 /// Ditto 1632 1805 Cycle!(R) cycle(R)(ref R input, size_t index = 0) if (isStaticArray!R) 1633 1806 { 1634 1807 return Cycle!(R)(input, index); 1635 1808 } 1636 1809 1637 1810 unittest 1638 1811 { 1639 1812 assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][])); 1640 1813 static assert(isForwardRange!(Cycle!(uint[]))); 1641 1642 struct DummyRange {1643 uint num;1644 ref uint front() { return num; }1645 void popFront() { num++; }1646 @property bool empty() { return num >= 10; }1647 typeof(this) save() { return this; }1648 }1649 static assert(isForwardRange!(Cycle!(DummyRange)));1650 1814 1651 1815 int[3] a = [ 1, 2, 3 ]; 1652 1816 static assert(isStaticArray!(typeof(a))); 1653 1817 auto c = cycle(a); 1654 1818 assert(a.ptr == c._ptr); 1655 1819 assert(equal(take(cycle(a), 5), [ 1, 2, 3, 1, 2 ][])); 1656 1820 static assert(isForwardRange!(typeof(c))); 1657 1821 } 1658 1822 1659 1823 unittest // For infinite ranges
