| Index: tool/input_sdk/lib/core/iterable.dart
|
| diff --git a/tool/input_sdk/lib/core/iterable.dart b/tool/input_sdk/lib/core/iterable.dart
|
| index fd95c799d86a8846fa986e12e388b44109e03116..a1967b734fa4765064d7254bd9ef08bd5fccdfd3 100644
|
| --- a/tool/input_sdk/lib/core/iterable.dart
|
| +++ b/tool/input_sdk/lib/core/iterable.dart
|
| @@ -5,12 +5,30 @@
|
| part of dart.core;
|
|
|
| /**
|
| - * An object that uses an [Iterator] to serve objects one at a time.
|
| + * A collection of values, or "elements", that can be accessed sequentially.
|
| *
|
| - * You can iterate over all objects served by an Iterable object
|
| - * using the for-in loop construct.
|
| - * For example, you can iterate over all of the keys in a [Map],
|
| - * because Map keys are iterable.
|
| + * The elements of the iterable are accessed by getting an [Iterator]
|
| + * using the [iterator] getter, and using it to step through the values.
|
| + * Stepping with the iterator is done by calling [Iterator.moveNext],
|
| + * and if the call returns `true`,
|
| + * the iterator has now moved to the next element,
|
| + * which is then available as [Iterator.current].
|
| + * If the call returns `false`, there are no more elements,
|
| + * and `iterator.currrent` returns `null`.
|
| + *
|
| + * You can create more than one iterator from the same `Iterable`.
|
| + * Each time `iterator` is read, it returns a new iterator,
|
| + * and different iterators can be stepped through independently,
|
| + * each giving access to all the elements of the iterable.
|
| + * The iterators of the same iterable *should* provide the same values
|
| + * in the same order (unless the underlying collection is modified between
|
| + * the iterations, which some collections allow).
|
| + *
|
| + * You can also iterate over the elements of an `Iterable`
|
| + * using the for-in loop construct, which uses the `iterator` getter behind the
|
| + * scenes.
|
| + * For example, you can iterate over all of the keys of a [Map],
|
| + * because `Map` keys are iterable.
|
| *
|
| * Map kidsBooks = {'Matilda': 'Roald Dahl',
|
| * 'Green Eggs and Ham': 'Dr Seuss',
|
| @@ -19,34 +37,64 @@ part of dart.core;
|
| * print('$book was written by ${kidsBooks[book]}');
|
| * }
|
| *
|
| - * The [List] class and the [Set] class implement this interface,
|
| - * as do classes in the [dart:collection](#dart-collection) library.
|
| + * The [List] and [Set] classes are both `Iterable`,
|
| + * as are most classes in the [dart:collection](#dart-collection) library.
|
| + *
|
| + * Some [Iterable] collections can be modified.
|
| + * Adding an element to a `List` or `Set` will change which elements it
|
| + * contains, and adding a new key to a `Map` changes the elements of [Map.keys].
|
| + * Iterators created after the change will provide the new elements, and may
|
| + * or may not preserve the order of existing elements
|
| + * (for example, a [HashSet] may completely change its order when a single
|
| + * element is added).
|
| + *
|
| + * Changing a collection *while* it is being iterated
|
| + * is generally *not* allowed.
|
| + * Doing so will break the iteration, which is typically signalled
|
| + * by throwing a [ConcurrentModificationError]
|
| + * the next time [Iterator.moveNext] is called.
|
| + * The current value of [Iterator.current] getter
|
| + * should not be affected by the change in the collection,
|
| + * the `current` value was set by the previous call to [Iterator.moveNext].
|
| + *
|
| + * Some iterables compute their elements dynamically every time they are
|
| + * iterated, like the one returned by [Iterable.generate] or the iterable
|
| + * returned by a `sync*` generator function. If the computation doesn't depend
|
| + * on other objects that may change, then the generated sequence should be
|
| + * the same one every time it's iterated.
|
| *
|
| - * You can implement Iterable in your own class.
|
| - * If you do, then an instance of your Iterable class
|
| - * can be the right-hand side of a for-in construct.
|
| + * The members of `Iterable`, other than `iterator` itself,
|
| + * work by looking at the elements of the iterable.
|
| + * This can be implemented by running through the [iterator], but some classes
|
| + * may have more efficient ways of finding the result
|
| + * (like [last] or [length] on a [List], or [contains] on a [Set]).
|
| *
|
| - * Some subclasss of [Iterable] can be modified. It is generally not allowed
|
| - * to modify such collections while they are being iterated. Doing so will break
|
| - * the iteration, which is typically signalled by throwing a
|
| - * [ConcurrentModificationError] when it is detected.
|
| + * The methods that return another `Iterable` (like [map] and [where])
|
| + * are all *lazy* - they will iterate the original (as necessary)
|
| + * every time the returned iterable is iterated, and not before.
|
| + *
|
| + * Since an iterable may be iterated more than once, it's not recommended to
|
| + * have detectable side-effects in the iterator.
|
| + * For methods like [map] and [while], the returned iterable will execute the
|
| + * argument function on every iteration, so those functions should also not
|
| + * have side effects.
|
| */
|
| abstract class Iterable<E> {
|
| const Iterable();
|
|
|
| /**
|
| - * Creates an Iterable that generates its elements dynamically.
|
| + * Creates an `Iterable` that generates its elements dynamically.
|
| *
|
| - * The Iterators created by the Iterable count from
|
| - * zero to [:count - 1:] while iterating, and call [generator]
|
| - * with that index to create the next value.
|
| + * An `Iterator` created by [iterator] will count from
|
| + * zero to [:count - 1:], and call [generator]
|
| + * with each index in turn to create the next value.
|
| *
|
| * If [generator] is omitted, it defaults to an identity function
|
| * on integers `(int x) => x`, so it should only be omitted if the type
|
| * parameter allows integer values.
|
| *
|
| - * As an Iterable, [:new Iterable.generate(n, generator)):] is equivalent to
|
| - * [:const [0, ..., n - 1].map(generator):]
|
| + * As an `Iterable`, `new Iterable.generate(n, generator))` is equivalent to
|
| + * `const [0, ..., n - 1].map(generator)`.
|
| */
|
| factory Iterable.generate(int count, [E generator(int index)]) {
|
| if (count <= 0) return new EmptyIterable<E>();
|
| @@ -54,84 +102,166 @@ abstract class Iterable<E> {
|
| }
|
|
|
| /**
|
| + * Creates an empty iterable.
|
| + *
|
| + * The empty iterable has no elements, and iterating it always stops
|
| + * immediately.
|
| + */
|
| + const factory Iterable.empty() = EmptyIterable<E>;
|
| +
|
| + /**
|
| * Returns a new `Iterator` that allows iterating the elements of this
|
| * `Iterable`.
|
| *
|
| - * Modifying the underlying data after creating the new iterator
|
| - * may cause an error the next time [Iterator.moveNext] is called.
|
| + * Iterable classes may specify the iteration order of their elements
|
| + * (for example [List] always iterate in index order),
|
| + * or they may leave it unspecified (for example a hash-based [Set]
|
| + * may iterate in any order).
|
| + *
|
| + * Each time `iterator` is read, it returns a new iterator,
|
| + * which can be used to iterate through all the elements again.
|
| + * The iterators of the same iterable can be stepped through independently,
|
| + * but should return the same elements in the same order,
|
| + * as long as the underlying collection isn't changed.
|
| + *
|
| + * Modifying the collection may cause new iterators to produce
|
| + * different elements, and may change the order of existing elements.
|
| + * A [List] specifies its iteration order precisely,
|
| + * so modifying the list changes the iteration order predictably.
|
| + * A hash-based [Set] may change its iteration order completely
|
| + * when adding a new element to the set.
|
| + *
|
| + * Modifying the underlying collection after creating the new iterator
|
| + * may cause an error the next time [Iterator.moveNext] is called
|
| + * on that iterator.
|
| + * Any *modifiable* iterable class should specify which operations will
|
| + * break iteration.
|
| */
|
| Iterator<E> get iterator;
|
|
|
| /**
|
| * Returns a new lazy [Iterable] with elements that are created by
|
| - * calling `f` on the elements of this `Iterable`.
|
| + * calling `f` on each element of this `Iterable` in iteration order.
|
| *
|
| * 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.
|
| + *
|
| + * Methods on the returned iterable are allowed to omit calling `f`
|
| + * on any element where the result isn't needed.
|
| + * For example, [elementAt] may call `f` only once.
|
| */
|
| - Iterable/*<T>*/ map/*<T>*/(/*=T*/ f(E element));
|
| + Iterable/*<T>*/ map/*<T>*/(/*=T*/ f(E e)) =>
|
| + new MappedIterable<E, dynamic/*=T*/>(this, f);
|
|
|
| /**
|
| * Returns a new lazy [Iterable] with all elements that satisfy the
|
| * predicate [test].
|
| *
|
| + * The matching elements have the same order in the returned iterable
|
| + * as they have in [iterator].
|
| + *
|
| * This method returns a view of the mapped elements. As long as the
|
| * returned [Iterable] is not iterated over, the supplied function [test] will
|
| * not be invoked. Iterating will not cache results, and thus iterating
|
| * multiple times over the returned [Iterable] will invoke the supplied
|
| * function [test] multiple times on the same element.
|
| */
|
| - Iterable<E> where(bool test(E element));
|
| + Iterable<E> where(bool f(E element)) => new WhereIterable<E>(this, f);
|
|
|
| /**
|
| * Expands each element of this [Iterable] into zero or more elements.
|
| *
|
| * The resulting Iterable runs through the elements returned
|
| - * by [f] for each element of this, in order.
|
| + * by [f] for each element of this, in iteration order.
|
| *
|
| * The returned [Iterable] is lazy, and calls [f] for each element
|
| * of this every time it's iterated.
|
| */
|
| - Iterable/*<T>*/ expand/*<T>*/(Iterable/*<T>*/ f(E element));
|
| + Iterable/*<T>*/ expand/*<T>*/(Iterable/*<T>*/ f(E element)) =>
|
| + new ExpandIterable<E, dynamic/*=T*/>(this, f);
|
|
|
| /**
|
| * Returns true if the collection contains an element equal to [element].
|
| *
|
| + * This operation will check each element in order for being equal to
|
| + * [element], unless it has a more efficient way to find an element
|
| + * equal to [element].
|
| + *
|
| * The equality used to determine whether [element] is equal to an element of
|
| - * the iterable, depends on the type of iterable.
|
| + * the iterable defaults to the [Object.operator==] of the element.
|
| + *
|
| + * Some types of iterable may have a different equality used for its elements.
|
| * For example, a [Set] may have a custom equality
|
| - * (see, e.g., [Set.identical]) that its `contains` uses.
|
| + * (see [Set.identical]) that its `contains` uses.
|
| * Likewise the `Iterable` returned by a [Map.keys] call
|
| - * will likely use the same equality that the `Map` uses for keys.
|
| + * should use the same equality that the `Map` uses for keys.
|
| */
|
| - bool contains(Object element);
|
| + bool contains(Object element) {
|
| + for (E e in this) {
|
| + if (e == element) return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
|
|
| /**
|
| - * Applies the function [f] to each element of this collection.
|
| + * Applies the function [f] to each element of this collection in iteration
|
| + * order.
|
| */
|
| - void forEach(void f(E element));
|
| + void forEach(void f(E element)) {
|
| + for (E element in this) f(element);
|
| + }
|
|
|
| /**
|
| * Reduces a collection to a single value by iteratively combining elements
|
| * of the collection using the provided function.
|
| *
|
| + * The iterable must have at least one element.
|
| + * If it has only one element, that element is returned.
|
| + *
|
| + * Otherwise this method starts with the first element from the iterator,
|
| + * and then combines it with the remaining elements in iteration order,
|
| + * as if by:
|
| + *
|
| + * E value = iterable.first;
|
| + * iterable.skip(1).forEach((element) {
|
| + * value = combine(value, element);
|
| + * });
|
| + * return value;
|
| + *
|
| * Example of calculating the sum of an iterable:
|
| *
|
| * iterable.reduce((value, element) => value + element);
|
| *
|
| */
|
| - E reduce(E combine(E value, E element));
|
| + E reduce(E combine(E value, E element)) {
|
| + Iterator<E> iterator = this.iterator;
|
| + if (!iterator.moveNext()) {
|
| + throw IterableElementError.noElement();
|
| + }
|
| + E value = iterator.current;
|
| + while (iterator.moveNext()) {
|
| + value = combine(value, iterator.current);
|
| + }
|
| + return value;
|
| + }
|
|
|
| /**
|
| * Reduces a collection to a single value by iteratively combining each
|
| - * element of the collection with an existing value using the provided
|
| - * function.
|
| + * element of the collection with an existing value
|
| + *
|
| + * Uses [initialValue] as the initial value,
|
| + * then iterates through the elements and updates the value with
|
| + * each element using the [combine] function, as if by:
|
| *
|
| - * Use [initialValue] as the initial value, and the function [combine] to
|
| - * create a new value from the previous one and an element.
|
| + * var value = initialValue;
|
| + * for (E element in this) {
|
| + * value = combine(value, element);
|
| + * }
|
| + * return value;
|
| *
|
| * Example of calculating the sum of an iterable:
|
| *
|
| @@ -139,184 +269,357 @@ abstract class Iterable<E> {
|
| *
|
| */
|
| dynamic/*=T*/ fold/*<T>*/(var/*=T*/ initialValue,
|
| - dynamic/*=T*/ combine(var/*=T*/ previousValue, E element));
|
| + dynamic/*=T*/ combine(var/*=T*/ 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 [test]. Returns `false` otherwise.
|
| + * Checks whether every element of this iterable satisfies [test].
|
| + *
|
| + * Checks every element in iteration order, and returns `false` if
|
| + * any of them make [test] return `false`, otherwise returns `true`.
|
| */
|
| - bool every(bool test(E element));
|
| + bool every(bool f(E element)) {
|
| + for (E element in this) {
|
| + if (!f(element)) return false;
|
| + }
|
| + return true;
|
| + }
|
|
|
| /**
|
| * Converts each element to a [String] and concatenates the strings.
|
| *
|
| - * Converts each element to a [String] by calling [Object.toString] on it.
|
| - * Then concatenates the strings, optionally separated by the [separator]
|
| - * string.
|
| + * Iterates through elements of this iterable,
|
| + * converts each one to a [String] by calling [Object.toString],
|
| + * and then concatenates the strings, with the
|
| + * [separator] string interleaved between the elements.
|
| */
|
| String join([String separator = ""]) {
|
| + Iterator<E> iterator = this.iterator;
|
| + if (!iterator.moveNext()) return "";
|
| StringBuffer buffer = new StringBuffer();
|
| - buffer.writeAll(this, separator);
|
| + if (separator == null || separator == "") {
|
| + do {
|
| + buffer.write("${iterator.current}");
|
| + } while (iterator.moveNext());
|
| + } else {
|
| + buffer.write("${iterator.current}");
|
| + while (iterator.moveNext()) {
|
| + buffer.write(separator);
|
| + buffer.write("${iterator.current}");
|
| + }
|
| + }
|
| return buffer.toString();
|
| }
|
|
|
| +
|
| /**
|
| - * Returns true if one element of this collection satisfies the
|
| - * predicate [test]. Returns false otherwise.
|
| + * Checks whether any element of this iterable satisfies [test].
|
| + *
|
| + * Checks every element in iteration order, and returns `true` if
|
| + * any of them make [test] return `true`, otherwise returns false.
|
| */
|
| - bool any(bool test(E element));
|
| + bool any(bool f(E element)) {
|
| + for (E element in this) {
|
| + if (f(element)) return true;
|
| + }
|
| + return false;
|
| + }
|
|
|
| /**
|
| * Creates a [List] containing the elements of this [Iterable].
|
| *
|
| - * The elements are in iteration order. The list is fixed-length
|
| - * if [growable] is false.
|
| + * The elements are in iteration order.
|
| + * The list is fixed-length if [growable] is false.
|
| */
|
| - List<E> toList({ bool growable: true });
|
| + List<E> toList({ bool growable: true }) =>
|
| + new List<E>.from(this, growable: growable);
|
|
|
| /**
|
| * Creates a [Set] containing the same elements as this iterable.
|
| *
|
| * The set may contain fewer elements than the iterable,
|
| - * if the iterable contains the an element more than once,
|
| + * if the iterable contains an element more than once,
|
| * or it contains one or more elements that are equal.
|
| * The order of the elements in the set is not guaranteed to be the same
|
| * as for the iterable.
|
| */
|
| - Set<E> toSet();
|
| + 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
|
| + * Counting all elements may involve iterating through all elements and can
|
| * therefore be slow.
|
| + * Some iterables have a more efficient way to find the number of elements.
|
| */
|
| - int get length;
|
| + int get length {
|
| + assert(this is! EfficientLength);
|
| + int count = 0;
|
| + Iterator it = iterator;
|
| + while (it.moveNext()) {
|
| + count++;
|
| + }
|
| + return count;
|
| + }
|
|
|
| /**
|
| - * Returns true if there is no element in this collection.
|
| + * Returns `true` if there are no elements in this collection.
|
| + *
|
| + * May be computed by checking if `iterator.moveNext()` returns `false`.
|
| */
|
| - bool get isEmpty;
|
| + bool get isEmpty => !iterator.moveNext();
|
|
|
| /**
|
| * Returns true if there is at least one element in this collection.
|
| + *
|
| + * May be computed by checking if `iterator.moveNext()` returns `true`.
|
| */
|
| - bool get isNotEmpty;
|
| + bool get isNotEmpty => !isEmpty;
|
|
|
| - /**
|
| - * Returns an [Iterable] with at most [count] elements.
|
| + /**
|
| + * Returns a lazy iterable of the [count] first elements of this iterable.
|
| *
|
| * The returned `Iterable` may contain fewer than `count` elements, if `this`
|
| * contains fewer than `count` elements.
|
| *
|
| - * It is an error if `count` is negative.
|
| + * The elements can be computed by stepping through [iterator] until [count]
|
| + * elements have been seen.
|
| + *
|
| + * The `count` must not be negative.
|
| */
|
| - Iterable<E> take(int count);
|
| + Iterable<E> take(int count) {
|
| + return new TakeIterable<E>(this, count);
|
| + }
|
|
|
| /**
|
| - * Returns an Iterable that stops once [test] is not satisfied anymore.
|
| + * Returns a lazy iterable of the leading elements satisfying [test].
|
| *
|
| - * The filtering happens lazily. Every new Iterator of the returned
|
| - * Iterable starts iterating over the elements of `this`.
|
| + * The filtering happens lazily. Every new iterator of the returned
|
| + * iterable starts 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 does not
|
| - * get or provide any more elements.
|
| + * The elements can be computed by stepping through [iterator] until an
|
| + * element is found where `test(element)` is false. At that point,
|
| + * the returned iterable stops (its `moveNext()` returns false).
|
| */
|
| - Iterable<E> takeWhile(bool test(E value));
|
| + Iterable<E> takeWhile(bool test(E value)) {
|
| + return new TakeWhileIterable<E>(this, test);
|
| + }
|
|
|
| /**
|
| - * Returns an Iterable that skips the first [count] elements.
|
| + * Returns an Iterable that provides all but the first [count] elements.
|
| *
|
| + * When the returned iterable is iterated, it starts iterating over `this`,
|
| + * first skipping past the initial [count] elements.
|
| * If `this` has fewer than `count` elements, then the resulting Iterable is
|
| * empty.
|
| + * After that, the remaining elements are iterated in the same order as
|
| + * in this iterable.
|
| *
|
| - * It is an error if `count` is negative.
|
| + * The `count` must not be negative.
|
| */
|
| - Iterable<E> skip(int count);
|
| + Iterable<E> skip(int count) {
|
| + return new SkipIterable<E>(this, count);
|
| + }
|
|
|
| /**
|
| - * Returns an Iterable that skips elements while [test] is satisfied.
|
| + * Returns an Iterable that skips leading elements while [test] is satisfied.
|
| *
|
| * The filtering happens lazily. Every new Iterator of the returned
|
| * Iterable iterates over all elements of `this`.
|
| *
|
| - * As long as the iterator's elements satisfy [test] they are
|
| - * discarded. Once an element does not satisfy the [test] the iterator stops
|
| - * testing and uses every later element unconditionally. That is, the elements
|
| - * of the returned Iterable are the elements of `this` starting from the
|
| - * first element that does not satisfy [test].
|
| + * The returned iterable provides elements by iterating this iterable,
|
| + * but skipping over all initial elements where `test(element)` returns
|
| + * true. If all elements satisfy `test` the resulting iterable is empty,
|
| + * otherwise it iterates the remaining elements in their original order,
|
| + * starting with the first element for which `test(element)` returns false,
|
| */
|
| - Iterable<E> skipWhile(bool test(E value));
|
| + 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):]
|
| + * Throws a [StateError] if `this` is empty.
|
| + * Otherwise returns the first element in the iteration order,
|
| + * equivalent to `this.elementAt(0)`.
|
| */
|
| - E get first;
|
| + E get first {
|
| + Iterator<E> it = iterator;
|
| + if (!it.moveNext()) {
|
| + throw IterableElementError.noElement();
|
| + }
|
| + return it.current;
|
| + }
|
|
|
| /**
|
| * Returns the last element.
|
| *
|
| - * If `this` is empty throws a [StateError].
|
| + * Throws a [StateError] if `this` is empty.
|
| + * Otherwise may iterate through the elements and returns the last one
|
| + * seen.
|
| + * Some iterables may have more efficient ways to find the last element
|
| + * (for example a list can directly access the last element,
|
| + * without iterating through the previous ones).
|
| */
|
| - E get last;
|
| + E get last {
|
| + Iterator<E> it = iterator;
|
| + if (!it.moveNext()) {
|
| + throw IterableElementError.noElement();
|
| + }
|
| + E result;
|
| + do {
|
| + result = it.current;
|
| + } while(it.moveNext());
|
| + return result;
|
| + }
|
|
|
| /**
|
| - * Returns the single element in `this`.
|
| + * Checks that this iterable has only one element, and returns that element.
|
| *
|
| - * If `this` is empty or has more than one element throws a [StateError].
|
| + * Throws a [StateError] if `this` is empty or has more than one element.
|
| */
|
| - E get single;
|
| + E get single {
|
| + Iterator<E> it = iterator;
|
| + if (!it.moveNext()) throw IterableElementError.noElement();
|
| + E result = it.current;
|
| + if (it.moveNext()) throw IterableElementError.tooMany();
|
| + return result;
|
| + }
|
|
|
| /**
|
| * Returns the first element that satisfies the given predicate [test].
|
| *
|
| - * If none matches, the result of invoking the [orElse] function is
|
| - * returned. By default, when [orElse] is `null`, a [StateError] is
|
| - * thrown.
|
| + * Iterates through elements and returns the first to satsify [test].
|
| + *
|
| + * If no element satisfies [test], the result of invoking the [orElse]
|
| + * function is returned.
|
| + * If [orElse] is omitted, it defaults to throwing a [StateError].
|
| */
|
| - E firstWhere(bool test(E element), { E orElse() });
|
| + E firstWhere(bool test(E element), { E orElse() }) {
|
| + for (E element in this) {
|
| + if (test(element)) return element;
|
| + }
|
| + if (orElse != null) return orElse();
|
| + throw IterableElementError.noElement();
|
| + }
|
|
|
| /**
|
| * Returns the last element that satisfies the given predicate [test].
|
| *
|
| - * If none matches, the result of invoking the [orElse] function is
|
| - * returned. By default, when [orElse] is `null`, a [StateError] is
|
| - * thrown.
|
| + * An iterable that can access its elements directly may check its
|
| + * elements in any order (for example a list starts by checking the
|
| + * last element and then moves towards the start of the list).
|
| + * The default implementation iterates elements in iteration order,
|
| + * checks `test(element)` for each,
|
| + * and finally returns that last one that matched.
|
| + *
|
| + * If no element satsfies [test], the result of invoking the [orElse]
|
| + * function is returned.
|
| + * If [orElse] is omitted, it defaults to throwing a [StateError].
|
| */
|
| - E lastWhere(bool test(E element), {E orElse()});
|
| + E lastWhere(bool test(E element), {E orElse()}) {
|
| + 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 IterableElementError.noElement();
|
| + }
|
|
|
| /**
|
| - * Returns the single element that satisfies [test]. If no or more than one
|
| - * element match then a [StateError] is thrown.
|
| + * Returns the single element that satisfies [test].
|
| + *
|
| + * Checks all elements to see if `test(element)` returns true.
|
| + * If exactly one element satisfies [test], that element is returned.
|
| + * Otherwise, if there are no matching elements, or if there is more than
|
| + * one matching element, a [StateError] is thrown.
|
| */
|
| - E singleWhere(bool test(E element));
|
| + E singleWhere(bool test(E element)) {
|
| + E result = null;
|
| + bool foundMatching = false;
|
| + for (E element in this) {
|
| + if (test(element)) {
|
| + if (foundMatching) {
|
| + throw IterableElementError.tooMany();
|
| + }
|
| + result = element;
|
| + foundMatching = true;
|
| + }
|
| + }
|
| + if (foundMatching) return result;
|
| + throw IterableElementError.noElement();
|
| + }
|
|
|
| /**
|
| * Returns the [index]th element.
|
| *
|
| * The [index] must be non-negative and less than [length].
|
| + * Index zero represents the first element (so `iterable.elementAt(0)` is
|
| + * equivalent to `iterable.first`).
|
| + *
|
| + * May iterate through the elements in iteration order, skipping the
|
| + * first `index` elements and returning the next.
|
| + * Some iterable may have more efficient ways to find the element.
|
| + */
|
| + E elementAt(int index) {
|
| + if (index is! int) throw new ArgumentError.notNull("index");
|
| + RangeError.checkNotNegative(index, "index");
|
| + int elementIndex = 0;
|
| + for (E element in this) {
|
| + if (index == elementIndex) return element;
|
| + elementIndex++;
|
| + }
|
| + throw new RangeError.index(index, this, "index", null, elementIndex);
|
| + }
|
| +
|
| + /**
|
| + * Returns a string representation of (some of) the elements of `this`.
|
| *
|
| - * 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`.
|
| + * Elements are represented by their own `toString` results.
|
| + *
|
| + * The default representation always contains the first three elements.
|
| + * If there are less than a hundred elements in the iterable, it also
|
| + * contains the last two elements.
|
| + *
|
| + * If the resulting string isn't above 80 characters, more elements are
|
| + * included from the start of the iterable.
|
| + *
|
| + * The conversion may omit calling `toString` on some elements if they
|
| + * are known to not occur in the output, and it may stop iterating after
|
| + * a hundred elements.
|
| */
|
| - E elementAt(int index);
|
| + String toString() => IterableBase.iterableToShortString(this, '(', ')');
|
| }
|
|
|
| typedef E _Generator<E>(int index);
|
|
|
| -class _GeneratorIterable<E> extends IterableBase<E>
|
| +class _GeneratorIterable<E> extends Iterable<E>
|
| implements EfficientLength {
|
| final int _start;
|
| final int _end;
|
| final _Generator<E> _generator;
|
| +
|
| + /// Creates an iterable that builds the elements from a generator function.
|
| + ///
|
| + /// The [generator] may be null, in which case the default generator
|
| + /// enumerating the integer positions is used. This means that [int] must
|
| + /// be assignable to [E] when no generator is provided. In practice this means
|
| + /// that the generator can only be emitted when [E] is equal to `dynamic`,
|
| + /// `int`, or `num`. The constructor will check that the types match.
|
| _GeneratorIterable(this._end, E generator(int n))
|
| : _start = 0,
|
| - _generator = (generator != null) ? generator : _id;
|
| + // The `as` below is used as check to make sure that `int` is assignable
|
| + // to [E].
|
| + _generator = (generator != null) ? generator : _id as _Generator<E>;
|
|
|
| _GeneratorIterable.slice(this._start, this._end, this._generator);
|
|
|
|
|