docs: add documentation for conditional operator, fibers, break statements, and ranges; remove placeholder usage page

This commit is contained in:
Bob Nystrom
2014-04-11 17:45:20 +00:00
parent 5140d096a1
commit 71783400ae
9 changed files with 189 additions and 78 deletions
+47 -44
View File
@@ -42,50 +42,6 @@ A `for` loop has three components:
3. A *body*. This is a curly block or a single statement. It gets executed once for each iteration of the loop.
### Numeric ranges
As you can see, a `for` loop works with lists. To loop over a range of consecutive integers, or loop a fixed number of times, you can use a *range* expression, like so:
:::dart
for (i in 1..100) {
IO.print(i)
}
This will loop over the numbers from 1 to 100, including 100 itself. If you want to leave off the last value, use three dots instead of two:
:::dart
for (i in 1...100) {
IO.print(i)
}
This looks like some special "range" syntax in the `for` loop, but it's actually just a pair of operators. The `..` and `...` syntax are infix "range" operators. Like [other operators](method-calls.html), they are just special syntax for a regular method call.
The number type implements them and returns instances of a `Range` class. That class in turn knows how to iterate over a series of numbers.
### The iterator protocol
Lists and ranges cover the two most common kinds of loops, but you should also be able to walk over your own sequence-like objects. To enable that, the semantics of a `for` are defined in terms of an "iterator protocol". The loop itself doesn't know anything about lists or ranges, it just knows how to call two particular methods on the object that resulted from evaluating the sequence expression.
It works like this. First Wren evaluates the sequence expression and stores it in a hidden variable. In the first example, it will just be the list object. It also creates a hidden "iterator" variable and initializes it to `null`.
At the beginning of each iteration, it calls `iterate()` on the sequence, and passes in the iterator. (So in the first iteration, it always passes in `null`.) The sequence's job is to take that iterator and advance it to the next element in the sequence (or, in the case where it's `null`, to advance it to the *first* element). It then returns either the new iterator, or `false` to indicate that there are no more elements.
If `false` is returned, Wren exits out of the loop and we're done. If anything else is returned, that means that we have advanced to a new valid element. To get that, Wren then calls `iteratorValue()` on the sequence and passes in the iterator value that it just got from calling `iterate()`. The sequence uses that to look up and return the appropriate element.
In other words, from Wren's perspective, the example loop looks something like this:
:::dart
{
var iter_
var seq_ = ["george", "john", "paul", "ringo"]
while (iter_ = seq_.iterate(iter_)) {
var beatle = seq_.iteratorValue(iter_)
IO.print(beatle)
}
}
The built-in List and Range types implement `iterate()` and `iteratorValue()` to walk over their respective sequences. You can implement the same methods in your classes to make your own types iterable.
## Break statements
Sometimes, right in the middle of a loop body, you decide you want to bail out and stop. To do that, you can use a `break` statement. It's just the `break` keyword all by itself. That will immediately exit out of the nearest enclosing `while` or `for` loop.
@@ -97,3 +53,50 @@ Sometimes, right in the middle of a loop body, you decide you want to bail out a
}
So this program will print the numbers from 1 to 3, but will not print 4.
## Numeric ranges
Lists are one common use for `for` loops, but sometimes you want to walk over a sequence of numbers, or loop a number of times. For that, you can use a *range* expression, like so:
:::dart
for (i in 1..100) {
IO.print(i)
}
This loops over the numbers from 1 to 100, including 100 itself. If you want to leave off the last value, use three dots instead of two:
:::dart
for (i in 1...100) {
IO.print(i)
}
This looks like some special "range" syntax in the `for` loop, but it's actually just a pair of operators. The `..` and `...` syntax are infix "range" operators. Like [other operators](method-calls.html), they are just special syntax for a regular method call. The number type implements them and returns instances of a `Range` class. That class in turn knows how to iterate over a series of numbers.
## The iterator protocol
Lists and ranges cover the two most common kinds of loops, but you should also be able to define your own sequences. To enable that, the semantics of a `for` are defined in terms of an "iterator protocol". The loop itself doesn't know anything about lists or ranges, it just knows how to call two particular methods on the object that resulted from evaluating the sequence expression.
When you write a loop like this:
:::dart
for (i in 1..100) {
IO.print(i)
}
Wren sees it something like this:
:::dart
var iter_ = null
var seq_ = 1..100
while (iter_ = seq_.iterate(iter_)) {
var i = seq_.iteratorValue(iter_)
IO.print(i)
}
First, Wren evaluates the sequence expression and stores it in a hidden variable (written `seq_` in the example but in reality it doesn't have a name you can use). It also creates a hidden "iterator" variable and initializes it to `null`.
Each iteration, it calls `iterate()` on the sequence, passing in the current iterator value. (In the first iteration, it passes in `null`.) The sequence's job is to take that iterator and advance it to the next element in the sequence. (Or, in the case where the iterator is `null`, to advance it to the *first* element). It then returns either the new iterator, or `false` to indicate that there are no more elements.
If `false` is returned, Wren exits out of the loop and we're done. If anything else is returned, that means that we have advanced to a new valid element. To get that, Wren then calls `iteratorValue()` on the sequence and passes in the iterator value that it just got from calling `iterate()`. The sequence uses that to look up and return the appropriate element.
The built-in List and Range types implement `iterate()` and `iteratorValue()` to walk over their respective sequences. You can implement the same methods in your classes to make your own types iterable.