@@ 1,7 1,6 @@
-//! This module contains iterators for use in while loops. Some
-//! iterators accept other iterators. Any struct that exposes a "next"
-//! method can be used with most of those, though some also require
-//! "reset".
+//! This module contains iterators for use in while loops, and some
+//! functions that operate on them. Some iterators accept other
+//! iterators.
//!
//! To use any iterator in this module, follow this pattern:
//! var specialized = IterType(args).init();
@@ 15,29 14,138 @@
//! shared across threads.
const std = @import("std");
+/// Iterator interface. Just passes calls straight on from next to
+/// nextFn and from reset to resetFn.
pub fn Iterator(comptime T: type) type {
return struct {
const Self = @This();
+
+ /// Returns null when iterator is exhausted.
nextFn: fn (self: *Self) ?T,
+
+ /// Resets the iterator to its initial state.
resetFn: fn (self: *Self) void,
+
+ /// Returns null when iterator is exhausted.
pub fn next(self: *Self) ?T {
return self.nextFn(self);
}
+
+ /// Resets the iterator to its initial state.
pub fn reset(self: *Self) void {
return self.resetFn(self);
}
};
}
-/// Half-open range type. Call next() on it to get values out. Example
-/// usage:
+/// An iterator that repeats a single value, either forever or a
+/// certain number of times.
+pub fn Repeat(comptime T: type) type {
+ return struct {
+ const Self = @This();
+
+ /// Take the address of this to get a usable Iterator.
+ iterator: Iterator(T),
+ val: T,
+ limit: ?struct { count: usize, sent: usize },
+
+ /// Return the value, or null if finite and exhausted.
+ pub fn next(iterator: *Iterator(T)) ?T {
+ const self = @fieldParentPtr(Self, "iterator", iterator);
+ if (self.limit) |limit| {
+ if (limit.count == limit.sent) {
+ return null;
+ }
+ self.limit = .{ .count = limit.count, .sent = limit.sent + 1 };
+ }
+ return self.val;
+ }
+
+ /// Does nothing if the iterator is infinite. Otherwise resets
+ /// the count.
+ pub fn reset(iterator: *Iterator(T)) void {
+ const self = @fieldParentPtr(Self, "iterator", iterator);
+ if (self.limit) |limit| {
+ self.limit = .{ .count = limit.count, .sent = 0 };
+ }
+ }
+
+ /// val is the value repeated. If count is null, this iterator
+ /// is infinete. Otherwise, it repeats count times.
+ pub fn init(val: T, count: ?usize) Self {
+ if (count) |cnt| {
+ return Self{
+ .iterator = Iterator(T){
+ .nextFn = next,
+ .resetFn = reset,
+ },
+ .val = val,
+ .limit = .{ .count = cnt, .sent = 0 },
+ };
+ } else {
+ return Self{
+ .iterator = Iterator(T){
+ .nextFn = next,
+ .resetFn = reset,
+ },
+ .val = val,
+ .limit = null,
+ };
+ }
+ }
+ };
+}
+
+/// Count unboundedly, starting at start by step. Note that this will
+/// invoke safety-checked undefined behavior if T is allowed to overflow.
+pub fn Count(comptime T: type) type {
+ return struct {
+ const Self = @This();
+
+ /// Take the address of this to get a usable Iterator.
+ iterator: Iterator(T),
+ next_val: T,
+ start: T,
+ step: T,
+
+ /// This is an "infinite" counter, so it never returns
+ /// null. It will, however, invoke safety-checked illegal
+ /// behavior if T is allowed to overflow.
+ pub fn next(iterator: *Iterator(T)) ?T {
+ const self = @fieldParentPtr(Self, "iterator", iterator);
+ const v = self.next_val;
+ self.next_val += self.step;
+ return v;
+ }
+
+ /// Start over at whatever the initial value of start was.
+ pub fn reset(iterator: *Iterator(T)) void {
+ const self = @fieldParentPtr(Self, "iterator", iterator);
+ self.next_val = self.start;
+ }
+
+ /// Return an instance that starts with start and counts by step.
+ /// For example, init(7, 3) would yield 7, 10, 13...
+ pub fn init(start: T, step: T) Self {
+ return Self{
+ .iterator = Iterator(T){ .nextFn = next, .resetFn = reset },
+ .next_val = start,
+ .start = start,
+ .step = step,
+ };
+ }
+ };
+}
+
+/// Half-open range type. Call next() on its iterator to get values
+/// out. Example usage:
///
-/// var range = try Range(u32, 1).init(0, 10);
+/// var range = try Range(u32).init(0, 10, 1);
/// var iter = &range.iterator;
/// while (iter.next()) |n| {
/// std.debug.warn("{}\n", .{n});
/// }
-pub fn Range(comptime T: type, step: T) type {
+pub fn Range(comptime T: type) type {
// Some profiling results, as run on an i5-9600KF: This is
// consistently a good deal slower than
// u32 i = 0; while (i < limit) : (i += 1) {}
@@ 56,18 164,20 @@ pub fn Range(comptime T: type, step: T)
// loops down by about a factor of 2. These benchmarks should
// probably be re-run when the compiler is more stable.
return struct {
+ /// Take the address of this to get a usable Iterator.
iterator: Iterator(T),
next_val: T,
start: T,
+ step: T,
end: T,
const Self = @This();
- /// Return the next element in the range, or null if the range
- /// has been exhausted.
+ /// Return the next element in the range, or null if the last
+ /// element in the range has already been returned.
pub fn next(iterator: *Iterator(T)) ?T {
const self = @fieldParentPtr(Self, "iterator", iterator);
const rv = self.next_val;
- if (step < 0) {
+ if (self.step < 0) {
if (rv <= self.end) {
return null;
}
@@ 77,7 187,7 @@ pub fn Range(comptime T: type, step: T)
}
}
- self.next_val += step;
+ self.next_val += self.step;
return rv;
}
@@ 87,8 197,11 @@ pub fn Range(comptime T: type, step: T)
self.next_val = self.start;
}
- /// Initialize. Returns error if step size is invalid.
- pub fn init(start: T, end: T) !Self {
+ /// Initialize. Returns error if step size is invalid. Range
+ /// runs from start (inclusive) to end (exclusive),
+ /// incrementing by step. So init(2, 7, 3) would yield 2, then
+ /// 5, then null.
+ pub fn init(start: T, end: T, step: T) !Self {
if (step == 0) {
return error.ZeroStepSize;
}
@@ 96,6 209,7 @@ pub fn Range(comptime T: type, step: T)
.next_val = start,
.start = start,
.end = end,
+ .step = step,
.iterator = Iterator(T){
.nextFn = next,
.resetFn = reset,
@@ 152,29 266,40 @@ pub fn Reversed(comptime T: type) type {
pub fn SliceIter(comptime T: type) type {
return struct {
const Self = @This();
- idx = ?usize,
+ idx: ?usize,
slice: []const T,
+ iterator: Iterator(T),
/// Initialize with a slice.
pub fn init(slice: []const T) Self {
- return Self{ .idx = 0, .slice = slice };
+ return Self{
+ .idx = 0,
+ .slice = slice,
+ .iterator = Iterator(T){
+ .nextFn = next,
+ .resetFn = reset,
+ },
+ };
}
/// Reset the iterator to the start.
- pub fn reset(self: *Self) void {
+ pub fn reset(iterator: *Iterator(T)) void {
+ const self = @fieldParentPtr(Self, "iterator", iterator);
self.idx = 0;
}
/// Return the next element in the slice, or null if the whole
/// slice has been consumed.
- pub fn next(self: *Self) ?T {
- if (idx) |i| {
+ pub fn next(iterator: *Iterator(T)) ?T {
+ const self = @fieldParentPtr(Self, "iterator", iterator);
+ if (self.idx) |i| {
if (i < self.slice.len) {
- defer self.idx += 1;
+ self.idx = i + 1;
+ return self.slice[i];
} else {
- defer self.idx = null;
+ self.idx = null;
+ return null;
}
- return self.slice[i];
} else {
return null;
}
@@ 187,8 312,8 @@ pub fn SliceIter(comptime T: type) type
/// iterators to process is passed as num.
///
/// Sample usage:
-/// var a = try Range(u32, 1).init(0, 2);
-/// var b = try Range(u32, 2).init(4, 8);
+/// var a = try Range(u32).init(0, 2, 1);
+/// var b = try Range(u32).init(4, 8, 2);
/// var iterators = [_]*Iterator(u32){ &a.iterator, &b.iterator };
/// var product = Product(u32, 2).init(iterators);
/// var iter = &product.iterator;
@@ 203,11 328,14 @@ pub fn Product(comptime T: type, comptim
// all return the same type.
const Result = [num]T;
+ /// Take the address of this to get a usable Iterator.
iterator: Iterator(Result),
children: [num]*Iterator(T),
next: [num]T,
done: bool = false,
+ /// Return the next array of combinations, or null if the last
+ /// one has been returned.
pub fn next(iterator: *Iterator(Result)) ?Result {
const self = @fieldParentPtr(Self, "iterator", iterator);
if (self.done)
@@ 228,6 356,7 @@ pub fn Product(comptime T: type, comptim
return prev;
}
+ /// Start over from the beginning.
pub fn reset(iterator: *Iterator(Result)) void {
const self = @fieldParentPtr(Self, "iterator", iterator);
for (self.children) |child| {
@@ 235,8 364,9 @@ pub fn Product(comptime T: type, comptim
}
}
- /// Invokes safety-checked illegal behavior if any iterators
- /// are empty.
+ /// Initialize and return structure. args is an array of
+ /// iterators to combine. Invokes safety-checked illegal
+ /// behavior if any iterators in args are empty.
pub fn init(args: [num]*Iterator(T)) Self {
var rv = Self{
.iterator = Iterator(Result){
@@ 255,9 385,42 @@ pub fn Product(comptime T: type, comptim
};
}
+/// Apply fun to successive elements in iter. Initial value of acc is
+/// init.
+pub fn fold(
+ comptime T: type,
+ fun: fn (acc: T, val: T) T,
+ iter: *Iterator(T),
+ init: T,
+) T {
+ var accumulator: T = init;
+ while (iter.next()) |val| {
+ accumulator = fun(accumulator, val);
+ }
+ return accumulator;
+}
+
+/// Don't use this. It's broken.
+pub fn ffold(
+ comptime T: type,
+ fun: fn (acc: T, val: T) T,
+ iter: *Iterator(T),
+ init: T,
+) T {
+ if (iter.next()) |val| {
+ return @call(
+ .{ .modifier = .always_tail },
+ ffold,
+ .{ T, fun, iter, val },
+ );
+ } else {
+ return init;
+ }
+}
+
const testing = std.testing;
test "range ascend" {
- var range = try Range(u32, 1).init(0, 10);
+ var range = try Range(u32).init(0, 10, 1);
var iter = &range.iterator;
var correct: u32 = 0;
while (iter.next()) |n| {
@@ 269,7 432,7 @@ test "range ascend" {
}
test "range descend" {
- var range = try Range(i32, -1).init(10, 0);
+ var range = try Range(i32).init(10, 0, -1);
var iter = &range.iterator;
var correct: i32 = 10;
while (iter.next()) |n| {
@@ 281,7 444,7 @@ test "range descend" {
}
test "range skip" {
- var range = try Range(u32, 2).init(0, 10);
+ var range = try Range(u32).init(0, 10, 2);
var iter = &range.iterator;
var correct: u32 = 0;
while (iter.next()) |n| {
@@ 295,11 458,12 @@ test "range skip" {
test "range runtime" {
var start: u32 = 0;
while (start < 10) : (start += 1) {
- var range = try Range(u32, 1).init(start, 10);
+ var range = try Range(u32).init(start, 10, 1);
var iter = &range.iterator;
var correct: u32 = start;
while (iter.next()) |n| {
testing.expectEqual(correct, n);
+
correct += 1;
}
testing.expectEqual(correct, 10);
@@ 319,9 483,9 @@ test "reverse" {
}
test "product" {
- const R = Range(u32, 1);
- var a = try R.init(0, 10);
- var b = try R.init(10, 25);
+ const R = Range(u32);
+ var a = try R.init(0, 10, 1);
+ var b = try R.init(10, 25, 1);
var iterators = [_]*Iterator(u32){ &a.iterator, &b.iterator };
var product = Product(u32, 2).init(iterators);
@@ 341,3 505,59 @@ test "product" {
testing.expectEqual(correct_b, 25);
testing.expectEqual(pi.next(), null);
}
+
+test "count" {
+ var count = Count(u64).init(0, 3);
+ var iter = &count.iterator;
+ var correct: u64 = 0;
+ while (iter.next()) |c| {
+ testing.expectEqual(correct, c);
+ correct += 3;
+ if (correct > 150) break;
+ }
+ testing.expectEqual(correct, 153);
+}
+
+test "repeat" {
+ var repeat = Repeat(u64).init(8, 100);
+ var iter = &repeat.iterator;
+ var count: u64 = 0;
+ while (iter.next()) |eight| {
+ testing.expectEqual(eight, 8);
+ count += 1;
+ }
+ testing.expectEqual(count, 100);
+
+ repeat = Repeat(u64).init(7, null);
+ iter = &repeat.iterator;
+ count = 0;
+ while (iter.next()) |seven| {
+ testing.expectEqual(seven, 7);
+ count += 1;
+ if (count == 1000) break;
+ }
+ testing.expectEqual(count, 1000);
+}
+
+fn add(a: u32, b: u32) u32 {
+ return a + b;
+}
+
+test "fold over range" {
+ var range = try Range(u32).init(1, 10, 1);
+ var iter = &range.iterator;
+ const total = fold(u32, add, iter, 12);
+ testing.expectEqual(total, 12 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9);
+}
+
+test "SliceIter" {
+ const slice: []const u8 = "Text";
+ var slice_iter = SliceIter(u8).init(slice);
+ var iter = &slice_iter.iterator;
+ var idx: usize = 0;
+ while (iter.next()) |c| {
+ testing.expectEqual(c, slice[idx]);
+ idx += 1;
+ }
+ testing.expectEqual(idx, slice.len);
+}