Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(262)

Side by Side Diff: pkg/collection_helpers/lib/algorithms.dart

Issue 23567044: Add collection-helpers library. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * Operations on collections.
7 */
8 library dart.collection_helper.algorithms;
9
10 import "dart:math" show Random;
11
12 /** Version of [binarySearch] optimized for comparable keys */
13 int _comparableBinarySearch(List<Comparable> list, Comparable key,
14 bool location) {
15 int min = 0;
16 int max = list.length;
17 while (min < max) {
18 int mid = min + ((max - min) >> 1);
floitsch 2013/09/21 18:12:37 ~/ 2
Lasse Reichstein Nielsen 2013/09/24 20:23:53 All done.
19 var element = list[mid];
20 int comp = element.compareTo(key);
21 if (comp == 0) return mid;
22 if (comp < 0) {
23 min = mid + 1;
24 } else {
25 max = mid;
26 }
27 }
28 if (location) return min;
29 return -1;
30 }
31
32 /**
33 * Returns the position of the [key] in [sortedList], if it is there.
34 *
35 * If the list isn't sorted according to the [compare] function, the result
36 * is unpredicatable.
37 *
38 * If [compare] is omitted, it defaults to calling [Comparable.compareTo] on
39 * the objects.
40 *
41 * Returns -1 if [key] is not in the list by default.
42 * If [location] is true, instead returns the index where [key] would have
43 * been. That is, where inserting the key at the returned position would keep
44 * the list sorted.
45 */
46 int binarySearch(List sortedList, var key,
47 { int compare(var a, var b),
48 bool location: false
Lasse Reichstein Nielsen 2013/09/18 13:39:25 Consider having three different versions: - plain
floitsch 2013/09/21 18:12:37 location is not a boolean name. returnLocation? ne
49 }) {
50 if (compare == null) return _comparableBinarySearch(sortedList, key, location) ;
Lasse Reichstein Nielsen 2013/09/18 13:39:25 Whoops, long line.
51 int min = 0;
52 int max = sortedList.length;
53 while (min < max) {
54 int mid = min + ((max - min) >> 1);
55 var element = sortedList[mid];
56 int comp = compare(element, key);
57 if (comp == 0) return mid;
58 if (comp < 0) {
59 min = mid + 1;
60 } else {
61 max = mid;
62 }
63 }
64 if (location) return max;
65 return -1;
66 }
67
68
69 /**
70 * Shuffles a list randomly.
71 *
72 * A sub-range of a list can be shuffled by providing [start] and [end].319
73 */
74 void shuffle(List list, [int start = 0, int end = null]) {
kevmoo-old 2013/09/20 21:34:35 Optional argument for Random impl.
floitsch 2013/09/21 18:12:37 This should be moved as member method on List.
Lasse Reichstein Nielsen 2013/09/24 20:23:53 I'd prefer to keep this as simple as possible. No
75 Random random = new Random();
76 if (end == null) end = list.length;
77 int length = end - start;
78 while (length > 1) {
79 int pos = random.nextInt(length);
80 var tmp1 = list[start + pos];
81 var tmp2 = list[start + length - 1];
82 list[start + length - 1] = tmp1;
83 list[start + pos] = tmp2;
84 length--;
85 }
86 }
87
88
89 /**
90 * Reverses a list, or a part of a list, in-place.
91 */
92 void reverse(List list, [int start = 0, int end = null]) {
93 if (end == null) end = list.length;
94 _reverse(list, start, end);
95 }
96
97 // Internal helper function that assumes valid arguments.
98 void _reverse(List list, int start, int end) {
99 for (int i = start, j = end - 1; i < j; i++, j--) {
100 var tmp = list[i];
101 list[i] = list[j];
102 list[j] = tmp;
103 }
104 }
105
106 /**
107 * Sort a list using insertion sort.
Lasse Reichstein Nielsen 2013/09/18 13:39:25 Should document the behavior of insertion sort (lo
108 */
109 void insertionSort(List list,
110 { int compare(a, b),
111 int start: 0,
112 int end: null }) {
113 // If the same method could have both positional and named optional
114 // parameters, this should be (list, [start, end], {compare}).
115 if (end == null) end = list.length;
116 if (compare == null) compare = Comparable.compare;
117 _insertionSort(list, compare, start, end, start + 1);
118 }
119
120 /**
121 * Internal helper function that assumes arguments correct.
122 *
123 * Assumes that the elements up to [sortedUntil] (not inclusive) are
124 * already sorted. The [sortedUntil] values should always be at least
125 * `start + 1`.
126 */
127 void _insertionSort(List list, int compare(a, b), int start, int end,
128 int sortedUntil) {
129 for (int pos = sortedUntil; pos < end; pos++) {
130 int min = start;
131 int max = pos;
132 var element = list[pos];
133 while (min < max) {
floitsch 2013/09/21 18:12:37 I'm not convinced that binary search really is wor
Lasse Reichstein Nielsen 2013/09/24 20:23:53 I'm planning to use this insertionSort for TimSort
134 int mid = min + ((max - min) >> 1);
floitsch 2013/09/21 18:12:37 ~/ 2
135 int comparison = compare(element, list[mid]);
136 if (comparison < 0) {
137 max = mid;
138 } else {
139 min = mid + 1;
140 }
141 }
142 list.setRange(min + 1, pos + 1, list, min);
143 list[min] = element;
144 }
145 }
146
147 /** Limit below which merge sort defaults to insertion sort. */
148 const int _MERGE_SORT_LIMIT = 32;
149
150 void mergeSort(List list, {int start: 0, int end: null, int compare(a, b)}) {
Lasse Reichstein Nielsen 2013/09/18 13:39:25 Should have documentation.
151 if (end == null) end = list.length;
152 if (compare == null) compare = Comparable.compare;
153 int length = end - start;
154 if (length < 2) return;
155 if (length < _MERGE_SORT_LIMIT) {
156 _insertionSort(list, compare, start, end, start + 1);
157 return;
158 }
159 // Special case the first split instead of directly calling
160 // _mergeSort, because the _mergeSort requires its target to
161 // be different from its source, and it requires extra space
162 // of the same size as the list to sort.
163 // This split allows us to have only half as much extra space,
164 // and it ends up in the original place.
165 int middle = start + ((end - start) >> 1);
floitsch 2013/09/21 18:12:37 ~/ 2
166 int firstLength = middle - start;
167 int secondLength = end - middle;
168 List scratchSpace = new List(secondLength);
floitsch 2013/09/21 18:12:37 assert(secondLength >= firstLength);
Lasse Reichstein Nielsen 2013/09/24 20:23:53 Why assert? Documentation should be enough.
169 _mergeSort(list, compare, middle, end, scratchSpace, 0);
170 int firstTarget = end - firstLength;
171 _mergeSort(list, compare, start, middle, list, firstTarget);
172 _merge(compare,
173 list, firstTarget, end,
174 scratchSpace, 0, secondLength,
175 list, start);
176 }
177
178 /**
179 * Performs an insertion sort into a potentially different list than the
180 * one containing the original values.
181 *
182 * It will work in-place as well.
183 */
184 void _movingInsertionSort(List list, int compare(a, b), int start, int end,
185 List target, int targetOffset) {
186 int length = end - start;
187 if (length == 0) return;
188 target[targetOffset] = list[start];
189 for (int i = 1; i < length; i++) {
190 var element = list[start + i];
191 int min = targetOffset;
192 int max = targetOffset + i;
193 while (min < max) {
194 int mid = min + ((max - min) >> 1);
floitsch 2013/09/21 18:12:37 ~/ 2
195 if (compare(element, target[mid]) < 0) {
196 max = mid;
197 } else {
198 min = mid + 1;
199 }
200 }
201 target.setRange(min + 1, targetOffset + i + 1,
202 target, min);
203 target[min] = element;
204 }
205 }
206
207 /**
208 * Sorts [list] from [start] to [end] into [target] at [targetOffset].
209 *
210 * The `target` list must be able to contain the range from `start` to `end`
211 * after `targetOffset`.
212 *
213 * Allows target to be the same list as [list], as long as it's not
214 * overlapping the `start..end` range.
215 */
216 void _mergeSort(List list, int compare(a, b), int start, int end,
217 List target, int targetOffset) {
218 int length = end - start;
219 if (length < _MERGE_SORT_LIMIT) {
220 _movingInsertionSort(list, compare, start, end, target, targetOffset);
221 return;
222 }
223 int middle = start + (length >> 1);
floitsch 2013/09/21 18:12:37 ~/ 2
224 int firstLength = middle - start;
225 int secondLength = end - middle;
226 int targetMiddle = targetOffset + firstLength;
227 // Sort the second half into the end of the target area.
228 _mergeSort(list, compare, middle, end,
229 target, targetMiddle);
230 // sort the first half into the end of the source area.
floitsch 2013/09/21 18:12:37 Start with capital letter.
Lasse Reichstein Nielsen 2013/09/24 20:23:53 Done.
231 _mergeSort(list, compare, start, middle,
232 list, middle);
233 // merge the two parts into the target area.
floitsch 2013/09/21 18:12:37 ditto.
Lasse Reichstein Nielsen 2013/09/24 20:23:53 Done.
234 _merge(compare,
235 list, middle, middle + firstLength,
236 target, targetMiddle, targetMiddle + secondLength,
237 target, targetOffset);
238 }
239
240 /**
241 * Mergest two lists into a target list.
242 *
243 * One of the input lists may be positioned at the end of the target
244 * list.
245 *
246 * For equal object, elements from [firstList] are always preferred.
247 * This allows the merge to be stable if the first list contains elements
248 * that started out earlier than the ones in [secondList]
249 */
250 void _merge(int compare(a, b),
251 List firstList, int firstStart, int firstEnd,
252 List secondList, int secondStart, int secondEnd,
253 List target, int targetOffset) {
254 // No empty lists reaches here.
255 assert(firstStart < firstEnd);
256 assert(secondStart < secondEnd);
257 int cursor1 = firstStart;
258 int cursor2 = secondStart;
259 var firstElement = firstList[cursor1++];
260 var secondElement = secondList[cursor2++];
261 // Two nested breakable statements is a hack to allow moving two different
262 // finishing conditions outside the loop without having to retest the
263 // exit conditions. This is just to keep the loop short.
264 firstEmpty: {
265 secondEmpty: while (true) {
266 if (compare(firstElement, secondElement) <= 0) {
267 target[targetOffset++] = firstElement;
268 if (cursor1 == firstEnd) break firstEmpty;
269 firstElement = firstList[cursor1++];
270 } else {
271 target[targetOffset++] = secondElement;
272 if (cursor2 == secondEnd) break secondEmpty;
floitsch 2013/09/21 18:12:37 I think it's cleaner to have a restList, restOffse
Lasse Reichstein Nielsen 2013/09/24 20:23:53 If I have to do more than a single line of work to
273 secondElement = secondList[cursor2++];
274 }
275 }
276 // Second list empties first.
277 target[targetOffset++] = firstElement;
278 target.setRange(targetOffset, targetOffset + (firstEnd - cursor1),
279 firstList, cursor1);
280 return;
281 }
282 // First list empties first.
283 target[targetOffset++] = secondElement;
284 target.setRange(targetOffset, targetOffset + (secondEnd - cursor2),
285 secondList, cursor2);
286 }
OLDNEW
« no previous file with comments | « no previous file | pkg/collection_helpers/lib/all.dart » ('j') | pkg/collection_helpers/lib/wrappers.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698