Commit e26661fc authored by Tim McNamara's avatar Tim McNamara

Update logistic-map.md

parent d6247856
This example demonstrates how to create an infinitely long `Iterator`
for a [iterated function][]. We're using'a [logistic map][] as our function, for three reasons:
This post walks you through how to define an infinite iterator in the Rust programming language.
### Background: the logistic map
To make things both simple and interesting, our iterator will be for a [iterated function][].
We're using'a [logistic map][] as our function, for three reasons:
- it's very simple
- it's the gateway drug into chaos theory
......@@ -9,13 +14,70 @@ for a [iterated function][]. We're using'a [logistic map][] as our function, for
[iterated function]: https://en.wikipedia.org/wiki/Iterated_function
[logistic map]: https://en.wikipedia.org/wiki/Logistic_map
The function is remarkably simple, yet produces spectacularly unpredictable results when the `r` parameter falls between 3.5 and 4.0:
```
x_new = r * x * (1 - x)
```
The thing that makes the logistic map interesting is that the value of `x_new` becomes `x` in the next iteration.
For many values of `r`, `x_new` is fairly orderly. But as `r` approaches 4.0, it becomes very difficult to predict the next value of `x_new`.
Here is the path for `r` is 3.995 and `x` is 0.5: 0.99875, 0.00498, 0.01982, 0.07763, 0.2861, 0.8159, 0.6000, ...
If you're confused, that's great!
You're not supposed to be able to predict what the next value will be.
All you need is a data structure stores some state, in this case `x`,
that is then fed into the actual function at each step.
### What is an iterator?
My guess is that the computer science terminology for "iterator" comes from mathematics.
An iteration is a repeated application of a function.
Within software engineering, the Gang of Four's Design Patterns book introduced the term "iterator pattern" into common discourse.
The intent was to show that it's possible to use more sophisticated methods of iterating over a collection than purely index-based methods.
In Rust, an interator is a type `T` that implements [`std::iter::Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html), or `Iterator` for short.
`Iterator` includes several specialist methods, but the most important one has the following signature:
## Example 1: Implementing the logistic map as a Rust iterator
```rust
fn next(&mut self) -> Option<Self::Item>
```
If you're new to Rust, some of this syntax might look intimidating. Here's a 3 second guide to each part of the line.
- `fn`: keyword to define a function in Rust
- `next`: the name of the function
- `(...)`: the function's arguments
- `&mut`: the function takes read/write access to the parameter
- `self`: the object/instance that is implementing `Iterator`
- `->`: the return type indicator, e.g. what follows is returned from `next`
- `Option`: an enum containing either a data ("`Some(...)`")or a value that represents no more data ("`None`")
- `Self::Item`: It's an _associated type_ that is defined as part of the `Iterator` implementation. For out mathematical function, our iterator's associated type is a floating point number type, `f64`.
Here is a concrete example of what it means to satisfy `fn next(&mut self) -> Option<Self::Item>`, with the implementation details replaced with an imaginary function "`calculate()`":
```
impl Iterator for LogisticMap {
type Item = f64; // <-.
// | set Self::Item to f64
fn next(&mut self) -> Option<Self::Item> { // <-'
let new_x = calculate();
Some(new_x)
}
}
```
### Implementing the logistic map as a Rust iterator
The implentation at _Listing 1`_ breaks the problem into parts:
* data structure stores some state, in this case `x` and `r`. That structure, `LogisticMap`, will be repeatedly mutated over its lifetime.
* the mathematical work is pushed into a standalone function `logistic_map()`
* `LogisticMap::next()` takes care of updating its `x` value and returning the correct result
### Listing 1
```rust
fn logistic_map(x: f64, r: f64) -> f64 {
x * r * (1.0 - x)
......@@ -70,5 +132,20 @@ Output ([Try yourself][example-1])
> ]
> ```
### How do I implement an infinitely long iterator?
Oh, that's easy. Never return `None` from `T::next()`. As long as you're always returning `Some<<T::Item>`, there will be another iteration.
----
### Found a problem?
Please file an issue! I will be happy to look into it!
### Support the author!
If you've liked this post, then check out the book I'm writing: [_Rust in Action_][ria], published by Manning Publications.
[ria]: https://www.manning.com/books/rust-in-action?a_aid=rust&a_bid=0367c58f&chan=profile&utm_source=tim_mcnamara
[example-1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&code=fn%20logistic_map(x%3A%20f64%2C%20r%3A%20f64)%20-%3E%20f64%20%7B%0A%20%20%20%20x%20*%20r%20*%20(1.0%20-%20x)%0A%7D%0A%20%20%20%0Astruct%20LogisticMap%20%7B%0A%20%20%20%20r%3A%20f64%2C%0A%20%20%20%20x%3A%20f64%2C%0A%7D%0A%0Aimpl%20Iterator%20for%20LogisticMap%20%7B%0A%20%20%20%20type%20Item%20%3D%20f64%3B%0A%20%20%20%20%0A%20%20%20%20fn%20next(%26mut%20self)%20-%3E%20Option%3CSelf%3A%3AItem%3E%20%7B%0A%20%20%20%20%20%20%20%20let%20new_x%20%3D%20logistic_map(self.x%2C%20self.r)%3B%0A%20%20%20%20%20%20%20%20self.x%20%3D%20new_x%3B%0A%20%20%20%20%20%20%20%20Some(new_x)%0A%20%20%20%20%7D%20%0A%7D%0A%0Afn%20main()%20%7B%0A%20%20%20%20let%20res%3A%20Vec%3Cf64%3E%20%3D%20LogisticMap%7Br%3A%203.995%2C%20x%3A%200.5%7D.take(20).collect()%3B%0A%20%20%20%20println!(%22%7B%3A%23%3F%7D%22%2C%20res)%3B%0A%7D
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment