Move implementation from IterableBase to Iterable.

Keep IterableBase around, but just extend Iterable.

There is no good reason to have IterableBase different from Iterable. The only argument is to be consistent with ListBase, but that's also inconsistent with, e.g., Stream.
ListBase only exists because List uses the default List constructor, so it's not a good super-class. As a usability-tradeoff, the ListBase class exists as a super-class.
Iterable doesn't have the same problem, so applying that "trade-off" has no up-side.

(A better long-term solution would be to be able to designate another constructor as the default "super()" constructor to call, so List could be used as a superclass).

R=sgjesse@google.com

Review URL: https://codereview.chromium.org//1025573005

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@45096 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
lrn@google.com 2015-04-13 12:26:15 +00:00
parent 20f7e0c660
commit 0e22f7402f
14 changed files with 306 additions and 363 deletions

View file

@ -2147,7 +2147,7 @@ TEST_CASE(Debug_GetSupertype) {
Dart_Handle object_name = Dart_NewStringFromCString("Object");
Dart_Handle int_name = Dart_NewStringFromCString("int");
Dart_Handle set_name = Dart_NewStringFromCString("Set");
Dart_Handle iterable_name = Dart_NewStringFromCString("IterableBase");
Dart_Handle iterable_name = Dart_NewStringFromCString("Iterable");
Dart_Handle list_name = Dart_NewStringFromCString("List");
Dart_Handle object_type = Dart_GetType(core_lib, object_name, 0, NULL);

View file

@ -427,7 +427,7 @@ class _CustomHashMap<K, V> extends _HashMap<K, V> {
String toString() => Maps.mapToString(this);
}
class HashMapKeyIterable<E> extends IterableBase<E>
class HashMapKeyIterable<E> extends Iterable<E>
implements EfficientLength {
final _map;
HashMapKeyIterable(this._map);

View file

@ -92,7 +92,7 @@ class ConstantProtoMap<K, V> extends ConstantStringMap<K, V> {
'__proto__' == key ? _protoValue : jsPropertyAccess(_jsObject, key);
}
class _ConstantMapKeyIterable<K> extends IterableBase<K> {
class _ConstantMapKeyIterable<K> extends Iterable<K> {
ConstantStringMap<K, dynamic> _map;
_ConstantMapKeyIterable(this._map);

View file

@ -3972,7 +3972,7 @@ class SyncStarIterator implements Iterator {
/// An Iterable corresponding to a sync* method.
///
/// Each invocation of a sync* method will return a new instance of this class.
class SyncStarIterable extends IterableBase {
class SyncStarIterable extends Iterable {
// This is a function that will return a helper function that does the
// iteration of the sync*.
//

View file

@ -324,7 +324,7 @@ class LinkedHashMapCell {
LinkedHashMapCell(this.hashMapCellKey, this.hashMapCellValue);
}
class LinkedHashMapKeyIterable<E> extends IterableBase<E>
class LinkedHashMapKeyIterable<E> extends Iterable<E>
implements EfficientLength {
final _map;
LinkedHashMapKeyIterable(this._map);

View file

@ -206,210 +206,9 @@ abstract class IterableMixin<E> implements Iterable<E> {
* This class implements all methods of [Iterable] except [Iterable.iterator]
* in terms of `iterator`.
*/
abstract class IterableBase<E> implements Iterable<E> {
// TODO(lrn): Base this on IterableMixin if there ever becomes a way
// to combine const constructors and mixins.
abstract class IterableBase<E> extends Iterable<E> {
const IterableBase();
Iterable map(f(E element)) => new MappedIterable<E, dynamic>(this, f);
Iterable<E> where(bool f(E element)) => new WhereIterable<E>(this, f);
Iterable expand(Iterable f(E element)) =>
new ExpandIterable<E, dynamic>(this, f);
bool contains(Object element) {
for (E e in this) {
if (e == element) return true;
}
return false;
}
void forEach(void f(E element)) {
for (E element in this) f(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;
}
dynamic fold(var initialValue,
dynamic combine(var previousValue, E element)) {
var value = initialValue;
for (E element in this) value = combine(value, element);
return value;
}
bool every(bool f(E element)) {
for (E element in this) {
if (!f(element)) return false;
}
return true;
}
String join([String separator = ""]) {
Iterator<E> iterator = this.iterator;
if (!iterator.moveNext()) return "";
StringBuffer buffer = new StringBuffer();
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();
}
bool any(bool f(E element)) {
for (E element in this) {
if (f(element)) return true;
}
return false;
}
List<E> toList({ bool growable: true }) =>
new List<E>.from(this, growable: growable);
Set<E> toSet() => new Set<E>.from(this);
int get length {
assert(this is! EfficientLength);
int count = 0;
Iterator it = iterator;
while (it.moveNext()) {
count++;
}
return count;
}
bool get isEmpty => !iterator.moveNext();
bool get isNotEmpty => !isEmpty;
Iterable<E> take(int n) {
return new TakeIterable<E>(this, n);
}
Iterable<E> takeWhile(bool test(E value)) {
return new TakeWhileIterable<E>(this, test);
}
Iterable<E> skip(int n) {
return new SkipIterable<E>(this, n);
}
Iterable<E> skipWhile(bool test(E value)) {
return new SkipWhileIterable<E>(this, test);
}
E get first {
Iterator it = iterator;
if (!it.moveNext()) {
throw IterableElementError.noElement();
}
return it.current;
}
E get last {
Iterator it = iterator;
if (!it.moveNext()) {
throw IterableElementError.noElement();
}
E result;
do {
result = it.current;
} while(it.moveNext());
return result;
}
E get single {
Iterator it = iterator;
if (!it.moveNext()) throw IterableElementError.noElement();
E result = it.current;
if (it.moveNext()) throw IterableElementError.tooMany();
return result;
}
E firstWhere(bool test(E value), { E orElse() }) {
for (E element in this) {
if (test(element)) return element;
}
if (orElse != null) return orElse();
throw IterableElementError.noElement();
}
E lastWhere(bool test(E value), { 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();
}
E singleWhere(bool test(E value)) {
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();
}
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`.
*
* Elements are represented by their own `toString` results.
*
* The 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.
*/
String toString() => iterableToShortString(this, '(', ')');
/**
* Convert an `Iterable` to a string like [IterableBase.toString].
*
@ -470,131 +269,131 @@ abstract class IterableBase<E> implements Iterable<E> {
buffer.write(rightDelimiter);
return buffer.toString();
}
}
/** A set used to identify cyclic lists during toString() calls. */
static final List _toStringVisiting = [];
/** A set used to identify cyclic lists during toString() calls. */
final List _toStringVisiting = [];
/** Check if we are currently visiting `o` in a toString call. */
static bool _isToStringVisiting(Object o) {
for (int i = 0; i < _toStringVisiting.length; i++) {
if (identical(o, _toStringVisiting[i])) return true;
}
return false;
/** Check if we are currently visiting `o` in a toString call. */
bool _isToStringVisiting(Object o) {
for (int i = 0; i < _toStringVisiting.length; i++) {
if (identical(o, _toStringVisiting[i])) return true;
}
return false;
}
/**
* Convert elments of [iterable] to strings and store them in [parts].
*/
void _iterablePartsToStrings(Iterable iterable, List parts) {
/*
* This is the complicated part of [iterableToShortString].
* It is extracted as a separate function to avoid having too much code
* inside the try/finally.
*/
/// Try to stay below this many characters.
const int LENGTH_LIMIT = 80;
/// Always at least this many elements at the start.
const int HEAD_COUNT = 3;
/// Always at least this many elements at the end.
const int TAIL_COUNT = 2;
/// Stop iterating after this many elements. Iterables can be infinite.
const int MAX_COUNT = 100;
// Per entry length overhead. It's for ", " for all after the first entry,
// and for "(" and ")" for the initial entry. By pure luck, that's the same
// number.
const int OVERHEAD = 2;
const int ELLIPSIS_SIZE = 3; // "...".length.
int length = 0;
int count = 0;
Iterator it = iterable.iterator;
// Initial run of elements, at least HEAD_COUNT, and then continue until
// passing at most LENGTH_LIMIT characters.
while (length < LENGTH_LIMIT || count < HEAD_COUNT) {
if (!it.moveNext()) return;
String next = "${it.current}";
parts.add(next);
length += next.length + OVERHEAD;
count++;
}
/**
* Convert elments of [iterable] to strings and store them in [parts].
*/
static void _iterablePartsToStrings(Iterable iterable, List parts) {
/*
* This is the complicated part of [iterableToShortString].
* It is extracted as a separate function to avoid having too much code
* inside the try/finally.
*/
/// Try to stay below this many characters.
const int LENGTH_LIMIT = 80;
/// Always at least this many elements at the start.
const int HEAD_COUNT = 3;
/// Always at least this many elements at the end.
const int TAIL_COUNT = 2;
/// Stop iterating after this many elements. Iterables can be infinite.
const int MAX_COUNT = 100;
// Per entry length overhead. It's for ", " for all after the first entry,
// and for "(" and ")" for the initial entry. By pure luck, that's the same
// number.
const int OVERHEAD = 2;
const int ELLIPSIS_SIZE = 3; // "...".length.
String penultimateString;
String ultimateString;
int length = 0;
int count = 0;
Iterator it = iterable.iterator;
// Initial run of elements, at least HEAD_COUNT, and then continue until
// passing at most LENGTH_LIMIT characters.
while (length < LENGTH_LIMIT || count < HEAD_COUNT) {
if (!it.moveNext()) return;
String next = "${it.current}";
parts.add(next);
length += next.length + OVERHEAD;
count++;
}
String penultimateString;
String ultimateString;
// Find last two elements. One or more of them may already be in the
// parts array. Include their length in `length`.
var penultimate = null;
var ultimate = null;
// Find last two elements. One or more of them may already be in the
// parts array. Include their length in `length`.
var penultimate = null;
var ultimate = null;
if (!it.moveNext()) {
if (count <= HEAD_COUNT + TAIL_COUNT) return;
ultimateString = parts.removeLast();
penultimateString = parts.removeLast();
} else {
penultimate = it.current;
count++;
if (!it.moveNext()) {
if (count <= HEAD_COUNT + TAIL_COUNT) return;
ultimateString = parts.removeLast();
if (count <= HEAD_COUNT + 1) {
parts.add("$penultimate");
return;
}
ultimateString = "$penultimate";
penultimateString = parts.removeLast();
length += ultimateString.length + OVERHEAD;
} else {
penultimate = it.current;
ultimate = it.current;
count++;
if (!it.moveNext()) {
if (count <= HEAD_COUNT + 1) {
parts.add("$penultimate");
return;
}
ultimateString = "$penultimate";
penultimateString = parts.removeLast();
length += ultimateString.length + OVERHEAD;
} else {
// Then keep looping, keeping the last two elements in variables.
assert(count < MAX_COUNT);
while (it.moveNext()) {
penultimate = ultimate;
ultimate = it.current;
count++;
// Then keep looping, keeping the last two elements in variables.
assert(count < MAX_COUNT);
while (it.moveNext()) {
penultimate = ultimate;
ultimate = it.current;
count++;
if (count > MAX_COUNT) {
// If we haven't found the end before MAX_COUNT, give up.
// This cannot happen in the code above because each entry
// increases length by at least two, so there is no way to
// visit more than ~40 elements before this loop.
if (count > MAX_COUNT) {
// If we haven't found the end before MAX_COUNT, give up.
// This cannot happen in the code above because each entry
// increases length by at least two, so there is no way to
// visit more than ~40 elements before this loop.
// Remove any surplus elements until length, including ", ...)",
// is at most LENGTH_LIMIT.
while (length > LENGTH_LIMIT - ELLIPSIS_SIZE - OVERHEAD &&
count > HEAD_COUNT) {
length -= parts.removeLast().length + OVERHEAD;
count--;
}
parts.add("...");
return;
// Remove any surplus elements until length, including ", ...)",
// is at most LENGTH_LIMIT.
while (length > LENGTH_LIMIT - ELLIPSIS_SIZE - OVERHEAD &&
count > HEAD_COUNT) {
length -= parts.removeLast().length + OVERHEAD;
count--;
}
parts.add("...");
return;
}
penultimateString = "$penultimate";
ultimateString = "$ultimate";
length +=
ultimateString.length + penultimateString.length + 2 * OVERHEAD;
}
penultimateString = "$penultimate";
ultimateString = "$ultimate";
length +=
ultimateString.length + penultimateString.length + 2 * OVERHEAD;
}
}
// If there is a gap between the initial run and the last two,
// prepare to add an ellipsis.
String elision = null;
if (count > parts.length + TAIL_COUNT) {
// If there is a gap between the initial run and the last two,
// prepare to add an ellipsis.
String elision = null;
if (count > parts.length + TAIL_COUNT) {
elision = "...";
length += ELLIPSIS_SIZE + OVERHEAD;
}
// If the last two elements were very long, and we have more than
// HEAD_COUNT elements in the initial run, drop some to make room for
// the last two.
while (length > LENGTH_LIMIT && parts.length > HEAD_COUNT) {
length -= parts.removeLast().length + OVERHEAD;
if (elision == null) {
elision = "...";
length += ELLIPSIS_SIZE + OVERHEAD;
}
// If the last two elements were very long, and we have more than
// HEAD_COUNT elements in the initial run, drop some to make room for
// the last two.
while (length > LENGTH_LIMIT && parts.length > HEAD_COUNT) {
length -= parts.removeLast().length + OVERHEAD;
if (elision == null) {
elision = "...";
length += ELLIPSIS_SIZE + OVERHEAD;
}
}
if (elision != null) {
parts.add(elision);
}
parts.add(penultimateString);
parts.add(ultimateString);
}
if (elision != null) {
parts.add(elision);
}
parts.add(penultimateString);
parts.add(ultimateString);
}

View file

@ -29,7 +29,7 @@ part of dart.collection;
* and a constant time length getter.
*/
class LinkedList<E extends LinkedListEntry<E>>
extends IterableBase<E>
extends Iterable<E>
implements _LinkedListLink {
int _modificationCount = 0;

View file

@ -111,7 +111,7 @@ abstract class UnmodifiableMapBase<K, V> =
* It accesses the values by iterating over the keys of the map, and using the
* map's `operator[]` to lookup the keys.
*/
class _MapBaseValueIterable<V> extends IterableBase<V>
class _MapBaseValueIterable<V> extends Iterable<V>
implements EfficientLength {
final Map _map;
_MapBaseValueIterable(this._map);
@ -285,11 +285,11 @@ class Maps {
*/
static String mapToString(Map m) {
// Reuse the list in IterableBase for detecting toString cycles.
if (IterableBase._isToStringVisiting(m)) { return '{...}'; }
if (_isToStringVisiting(m)) { return '{...}'; }
var result = new StringBuffer();
try {
IterableBase._toStringVisiting.add(m);
_toStringVisiting.add(m);
result.write('{');
bool first = true;
m.forEach((k, v) {
@ -303,8 +303,8 @@ class Maps {
});
result.write('}');
} finally {
assert(identical(IterableBase._toStringVisiting.last, m));
IterableBase._toStringVisiting.removeLast();
assert(identical(_toStringVisiting.last, m));
_toStringVisiting.removeLast();
}
return result.toString();

View file

@ -256,7 +256,7 @@ class _DoubleLinkedQueueSentinel<E> extends _DoubleLinkedQueueEntry<E> {
*
* Allows constant time add, remove-at-ends and peek operations.
*/
class DoubleLinkedQueue<E> extends IterableBase<E> implements Queue<E> {
class DoubleLinkedQueue<E> extends Iterable<E> implements Queue<E> {
_DoubleLinkedQueueSentinel<E> _sentinel;
int _elementCount = 0;
@ -440,7 +440,7 @@ class _DoubleLinkedQueueIterator<E> implements Iterator<E> {
*
* The structure is efficient for any queue or stack usage.
*/
class ListQueue<E> extends IterableBase<E> implements Queue<E> {
class ListQueue<E> extends Iterable<E> implements Queue<E> {
static const int _INITIAL_CAPACITY = 8;
List<E> _table;
int _head;

View file

@ -599,8 +599,8 @@ abstract class _SplayTreeIterator<T> implements Iterator<T> {
T _getValue(_SplayTreeNode node);
}
class _SplayTreeKeyIterable<K> extends IterableBase<K>
implements EfficientLength {
class _SplayTreeKeyIterable<K> extends Iterable<K>
implements EfficientLength {
_SplayTree<K> _tree;
_SplayTreeKeyIterable(this._tree);
int get length => _tree._count;
@ -617,7 +617,7 @@ class _SplayTreeKeyIterable<K> extends IterableBase<K>
}
}
class _SplayTreeValueIterable<K, V> extends IterableBase<V>
class _SplayTreeValueIterable<K, V> extends Iterable<V>
implements EfficientLength {
SplayTreeMap<K, V> _map;
_SplayTreeValueIterable(this._map);

View file

@ -145,7 +145,7 @@ abstract class Iterable<E> {
* on any element where the result isn't needed.
* For example, [elementAt] may call `f` only once.
*/
Iterable map(f(E element));
Iterable map(f(E element)) => new MappedIterable<E, dynamic>(this, f);
/**
* Returns a new lazy [Iterable] with all elements that satisfy the
@ -160,7 +160,7 @@ abstract class Iterable<E> {
* 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.
@ -171,7 +171,8 @@ abstract class Iterable<E> {
* The returned [Iterable] is lazy, and calls [f] for each element
* of this every time it's iterated.
*/
Iterable expand(Iterable f(E element));
Iterable expand(Iterable f(E element)) =>
new ExpandIterable<E, dynamic>(this, f);
/**
* Returns true if the collection contains an element equal to [element].
@ -189,13 +190,21 @@ abstract class Iterable<E> {
* Likewise the `Iterable` returned by a [Map.keys] call
* 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 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
@ -219,7 +228,17 @@ abstract class Iterable<E> {
* 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
@ -241,7 +260,11 @@ abstract class Iterable<E> {
*
*/
dynamic fold(var initialValue,
dynamic combine(var previousValue, E element));
dynamic combine(var previousValue, E element)) {
var value = initialValue;
for (E element in this) value = combine(value, element);
return value;
}
/**
* Checks whether every element of this iterable satisfies [test].
@ -249,7 +272,12 @@ abstract class Iterable<E> {
* 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.
@ -260,18 +288,36 @@ abstract class Iterable<E> {
* [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();
}
/**
* 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].
@ -279,7 +325,8 @@ abstract class Iterable<E> {
* 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.
@ -290,7 +337,7 @@ abstract class Iterable<E> {
* 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].
@ -299,23 +346,31 @@ abstract class Iterable<E> {
* 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 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 a lazy iterable of the [count] first elements of this iterable.
*
* The returned `Iterable` may contain fewer than `count` elements, if `this`
@ -326,7 +381,9 @@ abstract class Iterable<E> {
*
* The `count` must not be negative.
*/
Iterable<E> take(int count);
Iterable<E> take(int n) {
return new TakeIterable<E>(this, n);
}
/**
* Returns a lazy iterable of the leading elements satisfying [test].
@ -338,7 +395,9 @@ abstract class Iterable<E> {
* 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 provides all but the first [count] elements.
@ -352,7 +411,9 @@ abstract class Iterable<E> {
*
* The `count` must not be negative.
*/
Iterable<E> skip(int count);
Iterable<E> skip(int n) {
return new SkipIterable<E>(this, n);
}
/**
* Returns an Iterable that skips leading elements while [test] is satisfied.
@ -366,7 +427,9 @@ abstract class Iterable<E> {
* 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.
@ -375,7 +438,13 @@ abstract class Iterable<E> {
* Otherwise returs the first element in the iteration order,
* equivalent to `(iterator..moveNext())..current`.
*/
E get first;
E get first {
Iterator it = iterator;
if (!it.moveNext()) {
throw IterableElementError.noElement();
}
return it.current;
}
/**
* Returns the last element.
@ -387,14 +456,30 @@ abstract class Iterable<E> {
* (for example a list can directly access the last element,
* without iterating through the previous ones).
*/
E get last;
E get last {
Iterator it = iterator;
if (!it.moveNext()) {
throw IterableElementError.noElement();
}
E result;
do {
result = it.current;
} while(it.moveNext());
return result;
}
/**
* Checks that this iterable has only one element, and returns that element.
*
* Throws a [StateError] if `this` is empty or has more than one element.
*/
E get single;
E get single {
Iterator 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].
@ -405,7 +490,13 @@ abstract class Iterable<E> {
* 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].
@ -421,7 +512,19 @@ abstract class Iterable<E> {
* 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].
@ -431,7 +534,21 @@ abstract class Iterable<E> {
* 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.
@ -444,12 +561,39 @@ abstract class Iterable<E> {
* first `index` elements and returning the next.
* Some iterable may have more efficient ways to find the element.
*/
E elementAt(int index);
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`.
*
* 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.
*/
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;

View file

@ -31,7 +31,7 @@ part of dart.core;
* iterating either the set itself or any [Iterable] that is backed by the set,
* such as the ones returned by methods like [where] and [map].
*/
abstract class Set<E> extends IterableBase<E> implements EfficientLength {
abstract class Set<E> extends Iterable<E> implements EfficientLength {
/**
* Creates an empty [Set].
*

View file

@ -603,7 +603,7 @@ abstract class String implements Comparable<String>, Pattern {
/**
* The runes (integer Unicode code points) of a [String].
*/
class Runes extends IterableBase<int> {
class Runes extends Iterable<int> {
final String string;
Runes(this.string);

View file

@ -24,7 +24,7 @@ abstract class EfficientLength {
* All other methods are implemented in terms of [length] and [elementAt],
* including [iterator].
*/
abstract class ListIterable<E> extends IterableBase<E>
abstract class ListIterable<E> extends Iterable<E>
implements EfficientLength {
int get length;
E elementAt(int i);
@ -346,7 +346,7 @@ class ListIterator<E> implements Iterator<E> {
typedef T _Transformation<S, T>(S value);
class MappedIterable<S, T> extends IterableBase<T> {
class MappedIterable<S, T> extends Iterable<T> {
final Iterable<S> _iterable;
final _Transformation<S, T> _f;
@ -416,7 +416,7 @@ class MappedListIterable<S, T> extends ListIterable<T>
typedef bool _ElementPredicate<E>(E element);
class WhereIterable<E> extends IterableBase<E> {
class WhereIterable<E> extends Iterable<E> {
final Iterable<E> _iterable;
final _ElementPredicate _f;
@ -445,7 +445,7 @@ class WhereIterator<E> extends Iterator<E> {
typedef Iterable<T> _ExpandFunction<S, T>(S sourceElement);
class ExpandIterable<S, T> extends IterableBase<T> {
class ExpandIterable<S, T> extends Iterable<T> {
final Iterable<S> _iterable;
final _ExpandFunction _f;
@ -488,7 +488,7 @@ class ExpandIterator<S, T> implements Iterator<T> {
}
}
class TakeIterable<E> extends IterableBase<E> {
class TakeIterable<E> extends Iterable<E> {
final Iterable<E> _iterable;
final int _takeCount;
@ -545,7 +545,7 @@ class TakeIterator<E> extends Iterator<E> {
}
}
class TakeWhileIterable<E> extends IterableBase<E> {
class TakeWhileIterable<E> extends Iterable<E> {
final Iterable<E> _iterable;
final _ElementPredicate _f;
@ -578,7 +578,7 @@ class TakeWhileIterator<E> extends Iterator<E> {
}
}
class SkipIterable<E> extends IterableBase<E> {
class SkipIterable<E> extends Iterable<E> {
final Iterable<E> _iterable;
final int _skipCount;
@ -638,7 +638,7 @@ class SkipIterator<E> extends Iterator<E> {
E get current => _iterator.current;
}
class SkipWhileIterable<E> extends IterableBase<E> {
class SkipWhileIterable<E> extends Iterable<E> {
final Iterable<E> _iterable;
final _ElementPredicate _f;
@ -672,7 +672,7 @@ class SkipWhileIterator<E> extends Iterator<E> {
/**
* The always empty [Iterable].
*/
class EmptyIterable<E> extends IterableBase<E> implements EfficientLength {
class EmptyIterable<E> extends Iterable<E> implements EfficientLength {
const EmptyIterable();
Iterator<E> get iterator => const EmptyIterator();