Index: sdk/lib/core/iterable.dart |
diff --git a/sdk/lib/core/iterable.dart b/sdk/lib/core/iterable.dart |
index 8e258859fd523320e1b22118a3f8148b9b6f62aa..d150ab32746d85e97fc1a4e0dbeae938d93e1c5e 100644 |
--- a/sdk/lib/core/iterable.dart |
+++ b/sdk/lib/core/iterable.dart |
@@ -17,8 +17,558 @@ part of dart.core; |
* be used as the right-hand side of a for-in construct. |
*/ |
abstract class Iterable<E> { |
+ const Iterable(); |
+ |
/** |
* Returns an [Iterator] that iterates over this [Iterable] object. |
*/ |
- Iterator<E> iterator(); |
+ Iterator<E> get iterator; |
+ |
+ /** |
+ * Returns a lazy [Iterable] where each element [:e:] of [this] is replaced |
+ * by the result of [:f(e):]. |
+ * |
+ * This method returns a view of the mapped elements. As long as the |
+ * returned [Iterable] is not iterated over, the supplied function [f] will |
+ * not be invoked. The transformed elements will not be cached. Iterating |
+ * multiple times over the the returned [Iterable] will invoke the supplied |
+ * function [f] multiple times on the same element. |
+ */ |
+ Iterable mappedBy(f(E element)) => new MappedIterable<E, dynamic>(this, f); |
+ |
+ /** |
+ * Returns a lazy [Iterable] with all elements that satisfy the |
+ * predicate [f]. |
+ * |
+ * This method returns a view of the mapped elements. As long as the |
+ * returned [Iterable] is not iterated over, the supplied function [f] will |
+ * not be invoked. Iterating will not cache results, and thus iterating |
+ * multiple times over the the returned [Iterable] will invoke the supplied |
+ * function [f] multiple times on the same element. |
+ */ |
+ Iterable<E> where(bool f(E element)) => new WhereIterable<E>(this, f); |
+ |
+ /** |
+ * Check whether the collection contains an element equal to [element]. |
+ */ |
+ bool contains(E element) { |
+ for (E e in this) { |
+ if (e == element) return true; |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Applies the function [f] to each element of this collection. |
+ */ |
+ void forEach(void f(E element)) { |
+ for (E element in this) f(element); |
+ } |
+ |
+ /** |
+ * Reduce a collection to a single value by iteratively combining each element |
+ * of the collection with an existing value using the provided function. |
+ * Use [initialValue] as the initial value, and the function [combine] to |
+ * create a new value from the previous one and an element. |
+ * |
+ * Example of calculating the sum of a collection: |
+ * |
+ * collection.reduce(0, (prev, element) => prev + element); |
+ */ |
+ dynamic reduce(var initialValue, |
+ dynamic combine(var previousValue, E element)) { |
+ var value = initialValue; |
+ for (E element in this) value = combine(value, element); |
+ return value; |
+ } |
+ |
+ /** |
+ * Returns true if every elements of this collection satisify the |
+ * predicate [f]. Returns false otherwise. |
+ */ |
+ bool every(bool f(E element)) { |
+ for (E element in this) { |
+ if (!f(element)) return false; |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * Convert each element to a [String] and concatenate the strings. |
+ * |
+ * Converts each element to a [String] by calling [Object.toString] on it. |
+ * Then concatenates the strings, optionally separated by the [separator] |
+ * string. |
+ */ |
+ String join([String separator]) { |
+ Iterator<E> iterator = this.iterator; |
+ if (!iterator.moveNext()) return ""; |
+ StringBuffer buffer = new StringBuffer(); |
+ if (separator == null || separator == "") { |
+ do { |
+ buffer.add("${iterator.current}"); |
+ } while (iterator.moveNext()); |
+ } else { |
+ buffer.add("${iterator.current}"); |
+ while (iterator.moveNext()) { |
+ buffer.add(separator); |
+ buffer.add("${iterator.current}"); |
+ } |
+ } |
+ return buffer.toString(); |
+ } |
+ |
+ /** |
+ * Returns true if one element of this collection satisfies the |
+ * predicate [f]. Returns false otherwise. |
+ */ |
+ bool any(bool f(E element)) { |
+ for (E element in this) { |
+ if (f(element)) return true; |
+ } |
+ return false; |
+ } |
+ |
+ List<E> toList() => new List<E>.from(this); |
+ Set<E> toSet() => new Set<E>.from(this); |
+ |
+ /** |
+ * Returns the number of elements in [this]. |
+ * |
+ * Counting all elements may be involve running through all elements and can |
+ * therefore be slow. |
+ */ |
+ int get length { |
+ int count = 0; |
+ Iterator it = iterator; |
+ while (it.moveNext()) { |
+ count++; |
+ } |
+ return count; |
+ } |
+ |
+ /** |
+ * Find the least element in the iterable. |
+ * |
+ * Returns null if the iterable is empty. |
+ * Otherwise returns an element [:x:] of this [Iterable] so that |
+ * [:x:] is not greater than [:y:] (that is, [:compare(x, y) <= 0:]) for all |
+ * other elements [:y:] in the iterable. |
+ * |
+ * The [compare] function must be a proper [Comparator<T>]. If a function is |
+ * not provided, [compare] defaults to [Comparable.compare]. |
+ */ |
+ E min([int compare(E a, E b)]) { |
+ if (compare == null) compare = Comparable.compare; |
+ Iterator it = iterator; |
+ if (!it.moveNext()) return null; |
+ E min = it.current; |
+ while (it.moveNext()) { |
+ E current = it.current; |
+ if (compare(min, current) > 0) min = current; |
+ } |
+ return min; |
+ } |
+ |
+ /** |
+ * Find the largest element in the iterable. |
+ * |
+ * Returns null if the iterable is empty. |
+ * Otherwise returns an element [:x:] of this [Iterable] so that |
+ * [:x:] is not smaller than [:y:] (that is, [:compare(x, y) >= 0:]) for all |
+ * other elements [:y:] in the iterable. |
+ * |
+ * The [compare] function must be a proper [Comparator<T>]. If a function is |
+ * not provided, [compare] defaults to [Comparable.compare]. |
+ */ |
+ E max([int compare(E a, E b)]) { |
+ if (compare == null) compare = Comparable.compare; |
+ Iterator it = iterator; |
+ if (!it.moveNext()) return null; |
+ E max = it.current; |
+ while (it.moveNext()) { |
+ E current = it.current; |
+ if (compare(max, current) < 0) max = current; |
+ } |
+ return max; |
+ } |
+ |
+ /** |
+ * Returns true if there is no element in this collection. |
+ */ |
+ bool get isEmpty => !iterator.moveNext(); |
+ |
+ /** |
+ * Returns an [Iterable] with at most [n] elements. |
+ * |
+ * The returned [Iterable] may contain fewer than [n] elements, if [this] |
+ * contains fewer than [n] elements. |
+ */ |
+ Iterable<E> take(int n) { |
+ return new TakeIterable<E>(this, n); |
+ } |
+ |
+ /** |
+ * Returns an [Iterable] that stops once [test] is not satisfied anymore. |
+ * |
+ * The filtering happens lazily. Every new [Iterator] of the returned |
+ * [Iterable] will start iterating over the elements of [this]. |
+ * When the iterator encounters an element [:e:] that does not satisfy [test], |
+ * it discards [:e:] and moves into the finished state. That is, it will not |
+ * ask or provide any more elements. |
+ */ |
+ Iterable<E> takeWhile(bool test(E value)) { |
+ return new TakeWhileIterable<E>(this, test); |
+ } |
+ |
+ /** |
+ * Returns an [Iterable] that skips the first [n] elements. |
+ * |
+ * If [this] has fewer than [n] elements, then the resulting [Iterable] will |
+ * be empty. |
+ */ |
+ Iterable<E> skip(int n) { |
+ return new SkipIterable<E>(this, n); |
+ } |
+ |
+ /** |
+ * Returns an [Iterable] that skips elements while [test] is satisfied. |
+ * |
+ * The filtering happens lazily. Every new [Iterator] of the returned |
+ * [Iterable] will iterate over all elements of [this]. |
+ * As long as the iterator's elements do not satisfy [test] they are |
+ * discarded. Once an element satisfies the [test] the iterator stops testing |
+ * and uses every element unconditionally. |
+ */ |
+ Iterable<E> skipWhile(bool test(E value)) { |
+ return new SkipWhileIterable<E>(this, test); |
+ } |
+ |
+ /** |
+ * Returns the first element. |
+ * |
+ * If [this] is empty throws a [StateError]. Otherwise this method is |
+ * equivalent to [:this.elementAt(0):] |
+ */ |
+ E get first { |
+ Iterator it = iterator; |
+ if (!it.moveNext()) { |
+ throw new StateError("No elements"); |
+ } |
+ return it.current; |
+ } |
+ |
+ /** |
+ * Returns the last element. |
+ * |
+ * If [this] is empty throws a [StateError]. |
+ */ |
+ E get last { |
+ Iterator it = iterator; |
+ if (!it.moveNext()) { |
+ throw new StateError("No elements"); |
+ } |
+ E result; |
+ do { |
+ result = it.current; |
+ } while(it.moveNext()); |
+ return result; |
+ } |
+ |
+ /** |
+ * Returns the single element in [this]. |
+ * |
+ * If [this] is empty or has more than one element throws a [StateError]. |
+ */ |
+ E get single { |
+ Iterator it = iterator; |
+ if (!it.moveNext()) throw new StateError("No elements"); |
+ E result = it.current; |
+ if (it.moveNext()) throw new StateError("More than one element"); |
+ return result; |
+ } |
+ |
+ /** |
+ * Returns the first element that satisfies the given predicate [f]. |
+ * |
+ * If none matches, the result of invoking the [orElse] function is |
+ * returned. By default, when [orElse] is `null`, a [StateError] is |
+ * thrown. |
+ */ |
+ E firstMatching(bool test(E value), { E orElse() }) { |
+ // TODO(floitsch): check that arguments are of correct type? |
+ for (E element in this) { |
+ if (test(element)) return element; |
+ } |
+ if (orElse != null) return orElse(); |
+ throw new StateError("No matching element"); |
+ } |
+ |
+ /** |
+ * Returns the last element that satisfies the given predicate [f]. |
+ * |
+ * If none matches, the result of invoking the [orElse] function is |
+ * returned. By default, when [orElse] is [:null:], a [StateError] is |
+ * thrown. |
+ */ |
+ E lastMatching(bool test(E value), {E orElse()}) { |
+ // TODO(floitsch): check that arguments are of correct type? |
+ E result = null; |
+ bool foundMatching = false; |
+ for (E element in this) { |
+ if (test(element)) { |
+ result = element; |
+ foundMatching = true; |
+ } |
+ } |
+ if (foundMatching) return result; |
+ if (orElse != null) return orElse(); |
+ throw new StateError("No matching element"); |
+ } |
+ |
+ /** |
+ * Returns the single element that satisfies [f]. If no or more than one |
+ * element match then a [StateError] is thrown. |
+ */ |
+ E singleMatching(bool test(E value)) { |
+ // TODO(floitsch): check that argument is of correct type? |
+ E result = null; |
+ bool foundMatching = false; |
+ for (E element in this) { |
+ if (test(element)) { |
+ if (foundMatching) { |
+ throw new StateError("More than one matching element"); |
+ } |
+ result = element; |
+ foundMatching = true; |
+ } |
+ } |
+ if (foundMatching) return result; |
+ throw new StateError("No matching element"); |
+ } |
+ |
+ /** |
+ * Returns the [index]th element. |
+ * |
+ * If [this] [Iterable] has fewer than [index] elements throws a |
+ * [RangeError]. |
+ * |
+ * Note: if [this] does not have a deterministic iteration order then the |
+ * function may simply return any element without any iteration if there are |
+ * at least [index] elements in [this]. |
+ */ |
+ E elementAt(int index) { |
+ if (index is! int || index < 0) throw new RangeError.value(index); |
+ int remaining = index; |
+ for (E element in this) { |
+ if (remaining == 0) return element; |
+ remaining--; |
+ } |
+ throw new RangeError.value(index); |
+ } |
+} |
+ |
+typedef T _Transformation<S, T>(S value); |
+ |
+class MappedIterable<S, T> extends Iterable<T> { |
+ final Iterable<S> _iterable; |
+ final _Transformation _f; |
+ |
+ MappedIterable(this._iterable, T this._f(S element)); |
+ |
+ Iterator<T> get iterator => new MappedIterator<S, T>(_iterable.iterator, _f); |
+ |
+ // Length related functions are independent of the mapping. |
+ int get length => _iterable.length; |
+ bool get isEmpty => _iterable.isEmpty; |
+} |
+ |
+class MappedIterator<S, T> extends Iterator<T> { |
+ T _current; |
+ final Iterator<S> _iterator; |
+ final _Transformation _f; |
+ |
+ MappedIterator(this._iterator, T this._f(S element)); |
+ |
+ bool moveNext() { |
+ if (_iterator.moveNext()) { |
+ _current = _f(_iterator.current); |
+ return true; |
+ } else { |
+ _current = null; |
+ return false; |
+ } |
+ } |
+ |
+ T get current => _current; |
+} |
+ |
+typedef bool _ElementPredicate<E>(E element); |
+ |
+class WhereIterable<E> extends Iterable<E> { |
+ final Iterable<E> _iterable; |
+ final _ElementPredicate _f; |
+ |
+ WhereIterable(this._iterable, bool this._f(E element)); |
+ |
+ Iterator<E> get iterator => new WhereIterator<E>(_iterable.iterator, _f); |
+} |
+ |
+class WhereIterator<E> extends Iterator<E> { |
+ final Iterator<E> _iterator; |
+ final _ElementPredicate _f; |
+ |
+ WhereIterator(this._iterator, bool this._f(E element)); |
+ |
+ bool moveNext() { |
+ while (_iterator.moveNext()) { |
+ if (_f(_iterator.current)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ E get current => _iterator.current; |
+} |
+ |
+class TakeIterable<E> extends Iterable<E> { |
+ final Iterable<E> _iterable; |
+ final int _takeCount; |
+ |
+ TakeIterable(this._iterable, this._takeCount) { |
+ if (_takeCount is! int || _takeCount < 0) { |
+ throw new ArgumentError(_takeCount); |
+ } |
+ } |
+ |
+ Iterator<E> get iterator { |
+ return new TakeIterator<E>(_iterable.iterator, _takeCount); |
+ } |
+} |
+ |
+class TakeIterator<E> extends Iterator<E> { |
+ final Iterator<E> _iterator; |
+ int _remaining; |
+ |
+ TakeIterator(this._iterator, this._remaining) { |
+ assert(_remaining is int && _remaining >= 0); |
+ } |
+ |
+ bool moveNext() { |
+ _remaining--; |
+ if (_remaining >= 0) { |
+ return _iterator.moveNext(); |
+ } |
+ _remaining = -1; |
+ return false; |
+ } |
+ |
+ E get current { |
+ if (_remaining < 0) return null; |
+ return _iterator.current; |
+ } |
+} |
+ |
+class TakeWhileIterable<E> extends Iterable<E> { |
+ final Iterable<E> _iterable; |
+ final _ElementPredicate _f; |
+ |
+ TakeWhileIterable(this._iterable, bool this._f(E element)); |
+ |
+ Iterator<E> get iterator { |
+ return new TakeWhileIterator<E>(_iterable.iterator, _f); |
+ } |
+} |
+ |
+class TakeWhileIterator<E> extends Iterator<E> { |
+ final Iterator<E> _iterator; |
+ final _ElementPredicate _f; |
+ bool _isFinished = false; |
+ |
+ TakeWhileIterator(this._iterator, bool this._f(E element)); |
+ |
+ bool moveNext() { |
+ if (_isFinished) return false; |
+ if (!_iterator.moveNext() || !_f(_iterator.current)) { |
+ _isFinished = true; |
+ return false; |
+ } |
+ return true; |
+ } |
+ |
+ E get current { |
+ if (_isFinished) return null; |
+ return _iterator.current; |
+ } |
+} |
+ |
+class SkipIterable<E> extends Iterable<E> { |
+ final Iterable<E> _iterable; |
+ final int _skipCount; |
+ |
+ SkipIterable(this._iterable, this._skipCount) { |
+ if (_skipCount is! int || _skipCount < 0) { |
+ throw new ArgumentError(_skipCount); |
+ } |
+ } |
+ |
+ Iterable<E> skip(int n) { |
+ if (n is! int || n < 0) { |
+ throw new ArgumentError(n); |
+ } |
+ return new SkipIterable<E>(_iterable, _skipCount + n); |
+ } |
+ |
+ Iterator<E> get iterator { |
+ return new SkipIterator<E>(_iterable.iterator, _skipCount); |
+ } |
+} |
+ |
+class SkipIterator<E> extends Iterator<E> { |
+ final Iterator<E> _iterator; |
+ int _skipCount; |
+ |
+ SkipIterator(this._iterator, this._skipCount) { |
+ assert(_skipCount is int && _skipCount >= 0); |
+ } |
+ |
+ bool moveNext() { |
+ for (int i = 0; i < _skipCount; i++) _iterator.moveNext(); |
+ _skipCount = 0; |
+ return _iterator.moveNext(); |
+ } |
+ |
+ E get current => _iterator.current; |
+} |
+ |
+class SkipWhileIterable<E> extends Iterable<E> { |
+ final Iterable<E> _iterable; |
+ final _ElementPredicate _f; |
+ |
+ SkipWhileIterable(this._iterable, bool this._f(E element)); |
+ |
+ Iterator<E> get iterator { |
+ return new SkipWhileIterator<E>(_iterable.iterator, _f); |
+ } |
+} |
+ |
+class SkipWhileIterator<E> extends Iterator<E> { |
+ final Iterator<E> _iterator; |
+ final _ElementPredicate _f; |
+ bool _hasSkipped = false; |
+ |
+ SkipWhileIterator(this._iterator, bool this._f(E element)); |
+ |
+ bool moveNext() { |
+ if (!_hasSkipped) { |
+ _hasSkipped = true; |
+ while (_iterator.moveNext()) { |
+ if (!_f(_iterator.current)) return true; |
+ } |
+ } |
+ return _iterator.moveNext(); |
+ } |
+ |
+ E get current => _iterator.current; |
} |