From f822792a10bbe6d68e4620cb454f1682ad1ce7ed Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Thu, 19 Jun 2025 14:46:20 +0300 Subject: [PATCH] arrays: add reverse_iterator/1 + tests, allowing for `for child in arrays.reverse_iterator(children) {` instead of explicit C for style loop; it also avoids allocations (#24755) --- vlib/arrays/reverse_iterator.v | 37 ++++++++++++ vlib/arrays/reverse_iterator_test.v | 89 +++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 vlib/arrays/reverse_iterator.v create mode 100644 vlib/arrays/reverse_iterator_test.v diff --git a/vlib/arrays/reverse_iterator.v b/vlib/arrays/reverse_iterator.v new file mode 100644 index 0000000000..71173aca8b --- /dev/null +++ b/vlib/arrays/reverse_iterator.v @@ -0,0 +1,37 @@ +module arrays + +// ReverseIterator provides a convenient way to iterate in reverse over all elements of an array, +// without making allocations, using this syntax: `for elem in arrays.reverse_iterator(a) {` . +pub struct ReverseIterator[T] { +mut: + a []T + i int +} + +// reverse_iterator can be used to iterate over the elements in an array using this syntax: +// `for elem in arrays.reverse_iterator(a) {` . +pub fn reverse_iterator[T](a []T) ReverseIterator[T] { + return ReverseIterator[T]{ + a: a + i: a.len + } +} + +// next is the required method, to implement an iterator in V. +// It returns none when the iteration should stop. +// Otherwise it returns the current element of the array. +@[direct_array_access] +pub fn (mut iter ReverseIterator[T]) next() ?&T { + iter.i-- + if iter.i < 0 { + return none + } + return unsafe { &iter.a[iter.i] } +} + +// free frees the iterator resources. +pub fn (iter &ReverseIterator[T]) free() { + // The array stored in the iterator is not owned by the iterator. + // It should not be freed, when the iterator goes out of scope. + // This is the reason, that this manual free method exists and is empty. +} diff --git a/vlib/arrays/reverse_iterator_test.v b/vlib/arrays/reverse_iterator_test.v new file mode 100644 index 0000000000..8c0dec9cde --- /dev/null +++ b/vlib/arrays/reverse_iterator_test.v @@ -0,0 +1,89 @@ +import arrays + +struct Compound { +mut: + s string + i int + u u64 + m map[string]i16 +} + +fn check[T](original []T) { + mut result := []T{cap: original.len} + for x in arrays.reverse_iterator(original) { + result << x + } + assert result.len == original.len + assert result.first() == original.last() + assert result.reverse() == original + eprintln('> original: ${original}') + eprintln('> result: ${result}') +} + +fn test_reverse_iterator_basic() { + check(['abc', 'def', 'ghi', 'jkl']) + check([10, 20, 30, 40]) + check([ + Compound{'abc', 123, 444, { + 'aa': i16(12) + 'bb': 31 + }}, + Compound{'def', 456, 555, { + 'bb': i16(22) + 'cc': 32 + }}, + Compound{'xyz', 789, 666, { + 'cc': i16(32) + 'dd': 33 + }}, + ]) +} + +fn test_reverse_iterator_with_mut() { + mut original := [10, 20] + mut before := []int{cap: original.len} + mut after := []int{cap: original.len} + for mut x in arrays.reverse_iterator(original) { + before << *x + (**x)++ + after << *x + } + assert before == [20, 10] + assert after == [21, 11] + assert original == [11, 21] +} + +fn test_reverse_iterator_with_mut_compound() { + mut original := [Compound{ + s: 'abc' + i: 123 + }, Compound{ + s: 'xyz' + i: 987 + }] + mut before := []Compound{cap: original.len} + mut after := []Compound{cap: original.len} + for mut x in arrays.reverse_iterator(original) { + before << *x + x.i++ + x.s += ' tail' + x.u = 99 + x.m['modified'] = 1 + after << *x + } + assert after[0] == Compound{ + s: 'xyz tail' + i: 988 + u: 99 + m: { + 'modified': i16(1) + } + } + assert before[0] == Compound{ + s: 'xyz' + i: 987 + u: 0 + m: {} + } + assert after.reverse() == original +}