std.range
区間(Range)の反復とアダプタ。
use std.range区間リテラル
| 書き方 | 意味 |
|---|---|
a..b | a から b まで、刻み幅 1 |
a..b..s | a から b まで、刻み幅 s |
区間は終了値を含まない(左閉右開)。刻み幅は負の値も可能で、減少を表します。
イテレータプロトコル
iter は Result を返します。刻み幅が 0 の場合はエラー経路(E6009)となるため、事前に unwrap するか明示的に処理してください。
use std.assert
use std.list
use std.range
use std.result
main: () -> Void = {
it = result.unwrap(range.iter(1..4))
assert(range.has_next(it))
}ムーブセマンティクス:
has_nextとnextのシグネチャには&が付かず、イテレータをムーブ してしまいます。そのため、参照のたびにイテレータを作り直すか、for ... inで直接走査してください。これはstd.listのイテレータと一貫しています。
use std.assert
use std.range
use std.result
main: () -> Void = {
// 毎回新しいイテレータを作成
a = result.unwrap(range.iter(1..3))
assert(range.has_next(a))
b = result.unwrap(range.iter(1..3))
assert(range.next(b) == 1)
}日常の走査は for ... in を直接使います。
use std.assert
use std.list
main: () -> Void = {
nums = [1, 2, 3]
mut sum = 0
for x in nums {
sum = sum + x
}
assert(sum == 6)
}関数一覧
| 関数 | シグネチャ |
|---|---|
iter | (r: Range(Int)) -> Result(Iterator(Any), Error) |
has_next | (it: Iterator(Any)) -> Bool |
next | (it: &Iterator(Any)) -> Any |
contains | (r: Range(Int), x: Int) -> Result(Bool, Error) |
abort_invalid_step | (r: Range(Int)) -> Any |
map | (it: Iterator(Any), f: (Any) -> Any) -> Iterator(Any) |
filter | (it: Iterator(Any), p: (Any) -> Bool) -> Iterator(Any) |
collect | (it: Iterator(Any)) -> Vec(Any) |
reduce | (it: Iterator(Any), init: Any, f: (Any, Any) -> Any) -> Any |
for_each | (it: Iterator(Any), f: (Any) -> Void) -> Void |
iter
iter: (r: Range(Int)) -> Result(Iterator(Any), Error)区間からイテレータを作成します。
r—— 区間。例:1..6または3..0..-1
戻り値:成功時は Result.ok(イテレータ)。刻み幅が 0 の場合は Result.err となり、code は E6009 です。
use std.assert
use std.range
use std.result
main: () -> Void = {
it = result.unwrap(range.iter(1..4))
assert(range.has_next(it))
}has_next
has_next: (it: Iterator(Any)) -> Bool未消費の要素があるかどうかを判定します。
イテレータをムーブします。
use std.assert
use std.range
use std.result
main: () -> Void = {
it = result.unwrap(range.iter(1..4))
assert(range.has_next(it))
}next
next: (it: &Iterator(Any)) -> Any現在の要素を取り出し、内部カーソルを 1 つ進めます。
戻り値:現在の要素。反復終了時は Void を返します。
イテレータをムーブします。
use std.assert
use std.range
use std.result
main: () -> Void = {
it = result.unwrap(range.iter(1..4))
assert(range.next(it) == 1)
}減少区間も同様にサポートされます。
use std.assert
use std.range
use std.result
main: () -> Void = {
desc = result.unwrap(range.iter(3..0..-1))
assert(range.next(desc) == 3)
}contains
contains: (r: Range(Int), x: Int) -> Result(Bool, Error)x が区間内に含まれるかどうかを判定します。
r—— 区間x—— 判定対象の値
戻り値:Result.ok(Bool)。終了値は開区間(含まない)です。刻み幅付きの場合は、刻み幅に揃った要素のみが一致します。
use std.assert
use std.range
use std.result
main: () -> Void = {
assert(result.unwrap(range.contains(1..10, 5)))
assert(!result.unwrap(range.contains(1..10, 10))) // 終了値は含まない
assert(result.unwrap(range.contains(0..10..2, 4))) // 刻み幅に揃っている
assert(!result.unwrap(range.contains(0..10..2, 3))) // 揃っていない
}abort_invalid_step
abort_invalid_step: (r: Range(Int)) -> Any刻み幅が不正な場合に呼ばれる中止フック。for ... in が刻み幅 0 の区間を消費する際に呼び出されます。
常に E6007 を送出し、メッセージは Range step must be non-zero (for/in consumption) です。通常のコードから直接呼び出す必要はありません。
use std.range
main: () -> Void = {
// 単純に iter を使うと Err が返ってくるため、このフックを踏む必要はない
r = range.iter(1..3)
}アダプタ
map と filter は遅延アダプタを返します。直ちには計算せず、collect / reduce / for_each / for ... in によって消費された時点で結果が生成されます。
map
map: (it: Iterator(Any), f: (Any) -> Any) -> Iterator(Any)各要素に f を適用した新しい遅延イテレータを返します。
use std.assert
use std.list
use std.range
use std.result
main: () -> Void = {
doubled = range.collect(range.map(result.unwrap(range.iter(1..4)), x => x * 2))
assert(list.len(doubled) == 3)
assert(doubled[0] == 2)
}filter
filter: (it: Iterator(Any), p: (Any) -> Bool) -> Iterator(Any)p が真となる要素だけを保持する新しい遅延イテレータを返します。
use std.assert
use std.list
use std.range
use std.result
main: () -> Void = {
big = range.collect(range.filter(result.unwrap(range.iter(1..6)), x => x > 3))
assert(list.len(big) == 2)
assert(big[0] == 4)
}アダプタはチェーンして組み合わせられます。
use std.assert
use std.list
use std.range
use std.result
main: () -> Void = {
r = 1..6
chained = range.collect(range.map(range.filter(result.unwrap(range.iter(r)), x => x % 2 == 0), x => x * 10))
// 値セマンティクス:`chained` はインデックス読み出しで消費されるため、要素ごとに局所変数に束縛する
first = chained[0]
second = chained[1]
assert(first == 20)
assert(second == 40)
}collect
collect: (it: Iterator(Any)) -> Vec(Any)イテレータを消費し、全要素を List に集めます。
use std.assert
use std.list
use std.range
use std.result
main: () -> Void = {
xs = range.collect(result.unwrap(range.iter(1..4)))
assert(list.len(xs) == 3)
}reduce
reduce: (it: Iterator(Any), init: Any, f: (Any, Any) -> Any) -> Anyイテレータを消費して畳み込みます。
it—— イテレータinit—— 初期累積値f—— 畳み込み関数(累積値, 要素) -> 新しい累積値
引数の順序は
std.list.reduceと異なる点に注意してください。本モジュールは(イテレータ, 初期値, 関数)ですが、std.listは(リスト, 関数, 初期値)です。
use std.assert
use std.range
use std.result
main: () -> Void = {
total = range.reduce(result.unwrap(range.iter(1..6)), 0, (acc, x) => acc + x)
assert(total == 15)
}for_each
for_each: (it: Iterator(Any), f: (Any) -> Void) -> Void各要素に対して f を実行します。副作用目的で利用します。
use std.assert
use std.range
use std.result
main: () -> Void = {
// 1、2、3 を出力
range.for_each(result.unwrap(range.iter(1..4)), x => println(x))
assert(true)
}クロージャは現在のところ外側の
mut変数を捕捉して書き換えることはできません。そのため、for_eachで累積を行う方法は使えません(E1001が発生します)。累積にはreduceを使用してください。
関連
std.list—— リストとそのイテレータstd.result——iter/containsの戻り値をアンラップ- エラーコードリファレンス ——
E6009刻み幅不正
