std/iteration/iteration
std/iteration/src/iteration.trb
The pull side of a pipeline: Iterator, a cursor with one method, and Iterate, anything that can hand one out.
for, the spread operator (...) and every lazy stage (map, filter, take, ...) are built on these two
traits alone - a type takes part simply by implementing Iterate.
Iterator and Iterate are the synchronous half of the pipeline vocabulary: an Accumulator describes where the
values of a pipeline end up, and a Stage is the reusable middle a pipeline is built from. Source and Sink
carry the same two verbs (next, add/finish) asynchronously, for values that arrive over time instead of all
being there already.
Related
trait Iterator
trait Iterator<Item>
A cursor over a sequence, with one method: next. Iterators are stateful values, so whoever pulls from one holds
it in a var - copying one forks it into two independent cursors over the same remaining values.
Examples
var digits = [1, 2, 3].iterate()
print digits.next()
print digits.next()
Related
Iterate- anything that can hand out a fresh one.
fn next
var fn next(): Item?
The next value, or None once there is nothing left.
trait Length
trait Length
The size of a collection, and the two questions built on it. Every collection type implements it, so
isEmpty()/isNotEmpty() read the same everywhere.
Examples
const numbers: List<Int> = []
print numbers.isEmpty()
fn length
fn length(): Int
How many elements there are.
fn isEmpty
fn isEmpty(): Bool
Whether there are no elements.
fn isNotEmpty
fn isNotEmpty(): Bool
Whether there is at least one element.
trait Iterate
trait Iterate<Item>
Everything that works with for ... in and the spread operator (...). Only iterate is required.
A pipeline has three parts, like in Java and Rust:
source .filter { ... }.map { ... }.take(10) .toList()
any Iterate lazy stages: nothing runs, nothing is stored terminal operation: pulls the values through
Stages are values (an Iterate again), so a pipeline can be passed around, extended and iterated more than once.
Where the values end up is decided by the terminal operation alone - usually collect with a Collector.
Examples
const numbers = [1, 2, 3, 4, 5, 6]
const evenSquares = numbers.filter { _ % 2 == 0 }.map { _ * _ }
print evenSquares.toList()
Related
Length-length()/isEmpty(), for a collection that knows its size up front.Accumulator- whatcollect()hands the values to; every other terminal operation is written in terms of it.Stage- the reusable middle athrough(...)puts in front of the values.
fn iterate
fn iterate(): Iterator<Item>
A fresh cursor over the values, positioned before the first one.
fn map
fn map<Output>(transform: Transform<Item, Output>): Iterate<Output>
Transforms every value into another one, in order.
fn filter
fn filter(predicate: Predicate<Item>): Iterate<Item>
Keeps every value that answers the predicate, and drops the rest.
fn flatMap
fn flatMap<Output>(transform: Transform<Item, Iterate<Output>>): Iterate<Output>
Transforms every value into its own Iterate, and flattens all of them into one sequence.
fn filterMap
fn filterMap<Output>(transform: Transform<Item, Output?>): Iterate<Output>
map and filter in one: keeps the values of all Some. The bridge for functions that return an Option.
fn mapWhile
fn mapWhile<Output>(transform: Transform<Item, Output?>): Iterate<Output>
Like filterMap, but ends the pipeline at the first None.
fn take
fn take(amount: Int): Iterate<Item>
The first amount values. Ends the pipeline there, so a source that never runs out still finishes.
fn skip
fn skip(amount: Int): Iterate<Item>
Every value after the first amount, which are read and thrown away.
fn takeWhile
fn takeWhile(predicate: Predicate<Item>): Iterate<Item>
Values up to, but not including, the first one that fails the predicate.
fn zip
fn zip<Output>(other: Iterate<Output>): Iterate<(Item, Output)>
Pairs each value with the one at the same position of other; stops at the shorter of the two.
fn indexed
fn indexed(): Iterate<(index: Int, item: Item)>
(0, first), (1, second), ..., with the two halves named: pair.index and pair.item read better than
pair.0 and pair.1 wherever the pair is held instead of destructured.
Examples
for pair in ["a", "b"].indexed() {
print "{pair.index}: {pair.item}"
}
fn sorted
fn sorted<Key: Compare>(by: Transform<Item, Key>): Iterate<Item>
Lazy as a stage, but has to buffer all values as soon as it is iterated.
fn through
fn through<Output>(stage: Stage<Item, Output>): Iterate<Output>
Puts a Stage in front of the values: the one stage that is written once and works on a Source of std/stream
just as well (body.through(Json.items<User>())). Everything above is the same stage with a name of its own.
fn collect
fn collect<Output>(into: Accumulator<Item, Output>): Output
The general terminal operation: pulls the values into an Accumulator.
The accumulator is taken by value, so what it fills is a copy and the caller's own value is untouched: collecting
twice with one accumulator is two independent runs. isDone() is asked before the first value and after every
add, so an accumulator that has seen enough (taking, first) stops the pull instead of reading a source to
its end.
fn to
fn to<Target: From<Iterate<Item>>>(): Target
Collects into whatever can be created from an Iterate: .to<Set<Int>>(), or
const names: List<String> = pipeline.to(). No special trait needed, this is plain From<Iterate<Item>>.
fn toList
fn toList(): List<Item>
The values, collected into a List.
fn toSet
fn toSet(): Set<Item> where Item: Hash
The values, collected into a Set, which drops the duplicates.
fn joined
fn joined(separator: String = ""): String where Item: Show
["a", "b"].joined(separator: ", ") is "a, b". For String items, show() is the text itself.
Pitfalls
It costs one pass over the pieces and log n passes over the bytes, and it holds the pieces and one merged level at
the same time - so joining a gigabyte needs two of them. concatenated says why appending to one accumulator would
be worse.
fn forEach
fn forEach(action: Action<Item>)
Runs action once for every value, for a step that produces nothing itself.
fn fold
fn fold<State>(initial: State, combine: (State, Item) => State): State
Combines every value into one, left to right, starting from initial.
fn find
fn find(predicate: Predicate<Item>): Item?
The first value that answers the predicate, or None. Stops pulling as soon as one is found.
fn first
fn first(): Item?
The first value, or None when there is none.
fn any
fn any(predicate: Predicate<Item>): Bool
Whether at least one value answers the predicate. Stops at the first one that does.
fn all
fn all(predicate: Predicate<Item>): Bool
Whether every value answers the predicate. Stops at the first one that does not.
fn count
fn count(): Int
How many values there are. Reads every one, even where nothing about them is kept.
fn sum
fn sum(): Item where Item: Add & From<Int>
Every value, added together, starting from zero.
fn groupBy
fn groupBy<Key: Hash>(key: Transform<Item, Key>): Map<Key, List<Item>>
Shorthand for collect(groupingBy(key))