Reference

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 - what collect() hands the values to; every other terminal operation is written in terms of it.
  • Stage - the reusable middle a through(...) 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))