We recognize the good ol’ for
loop from the C-family of programming languages. It is available with the same familiar syntax and semantics in Dart, too:
for (int i = 0; i < 10; i++) {
print(i);
}
In addition to that, Dart has a for-in
loop, similar to Java in semantics (iterator()
, hasNext()
and next()
). for-in
is used to conveniently loop over any Iterable
sequence, such as a List
.
for (int i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) {
print(i);
}
If you’re into Python then you know that over there you for
loop over sequences exclusively, and use xrange
extensively.
for i in xrange(0, 10):
print(i)
xrange
is quite convenient and prevents a lot of variable name stuttering. It takes up to three parameters: start
, stop
and step
. Python xrange
s exclude the stop
endpoint and step
is 1
by default. start
is 0
by default so you’ll often see xrange(len(seq))
used in Python code to iterate over sequence indices.
Back to Dart. While good ol’ for
loops have been working fine for the last few decades or so, perhaps something xrange
-like could be fun here too. We’ve already got for-in
and iterators after all. Let’s create a Range
class that implements the Iterable
interface with a corresponding RangeIterator
and we’ll be able to do for-in range
, with top-level function range
being a wrapper around the Range
constructor. We’ll include both endpoints so range(0, 9)
means [0, 1, .. 9]
. Again step
defaults to 1
so range(0, 9)
is the same as range(0, 9, 1)
.
for (int i in range(0, 9)) {
print(i);
}
That looks swell. No surprise it has already been discussed on dart-misc. We can also pass a range to anything that expects an Iterable
. Here’s how to generate the List
[0, 2, .. 8]
using the List.from
constructor:
var list = new List<int>.from(range(0, 8, 2));
Iterating over indices is done like so:
var str = "hellojed";
for (int i in range(0, str.length - 1)) {
print("$i: ${str[i]}");
}
We’ll add a top-level function indices
to sweeten it a bit:
for (int i in indices(str)) {
print("$i: ${str[i]}");
}
We can make range
more useful still. The Collection
interface (implemented by List
and Set
among others) contains length
, every
, filter
, forEach
, isEmpty
and some
. Let’s implement those too. Now we can pass around range
s anywhere a Collection
is expected, for example to addAll
:
var queue = new Queue<int>();
// ...
queue.addAll(range(0, 9));
How about a List
with the numbers [0, 1, .. 50]
that are perfect squares:
bool isSquare(int n) => Math.pow(Math.sqrt(n).round(), 2) == n;
var squares = range(0, 50).filter(isSquare);
Or the other way around? map
isn’t part of Collection
but let’s add it anyways:
var squares = range(0, 7).map((e) => e * e);
The original example can also be rewritten like this, using forEach
:
range(0, 9).forEach(print);
Not too shabby, all in all. Good ol’ for
loops have less overhead than their for-in range
equivalent but the difference is nothing to write home about, especially if your loop body does something useful. Your personal style is probably more important - as for myself I consider range
a nice complement.
Check out the tests for more examples. The implementation should be easy to follow if you want to learn how to create a Collection
of your own. If you want a little Dart exercise then change Range
so that it only implements Iterable<int>
and remove everything Collection
-specific. Then add it back again guided by the unit tests, this time with your own code. Or start from scratch altogether.
Oh and the Dart for-in
loop is just (very convenient) syntactic sugar of something similar to this:
for (Iterator it = range(0, 9).iterator(); it.hasNext(); ) {
int i = it.next();
print(i);
}
dart-range source available on github.
Follow me on Twitter
« Dart syntax highlighting support for Pygments ↑ Home Dart syntax highlighting update »