Skip to content

Commit f822792

Browse files
authored
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)
1 parent 29188e9 commit f822792

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed

vlib/arrays/reverse_iterator.v

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module arrays
2+
3+
// ReverseIterator provides a convenient way to iterate in reverse over all elements of an array,
4+
// without making allocations, using this syntax: `for elem in arrays.reverse_iterator(a) {` .
5+
pub struct ReverseIterator[T] {
6+
mut:
7+
a []T
8+
i int
9+
}
10+
11+
// reverse_iterator can be used to iterate over the elements in an array using this syntax:
12+
// `for elem in arrays.reverse_iterator(a) {` .
13+
pub fn reverse_iterator[T](a []T) ReverseIterator[T] {
14+
return ReverseIterator[T]{
15+
a: a
16+
i: a.len
17+
}
18+
}
19+
20+
// next is the required method, to implement an iterator in V.
21+
// It returns none when the iteration should stop.
22+
// Otherwise it returns the current element of the array.
23+
@[direct_array_access]
24+
pub fn (mut iter ReverseIterator[T]) next() ?&T {
25+
iter.i--
26+
if iter.i < 0 {
27+
return none
28+
}
29+
return unsafe { &iter.a[iter.i] }
30+
}
31+
32+
// free frees the iterator resources.
33+
pub fn (iter &ReverseIterator[T]) free() {
34+
// The array stored in the iterator is not owned by the iterator.
35+
// It should not be freed, when the iterator goes out of scope.
36+
// This is the reason, that this manual free method exists and is empty.
37+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import arrays
2+
3+
struct Compound {
4+
mut:
5+
s string
6+
i int
7+
u u64
8+
m map[string]i16
9+
}
10+
11+
fn check[T](original []T) {
12+
mut result := []T{cap: original.len}
13+
for x in arrays.reverse_iterator(original) {
14+
result << x
15+
}
16+
assert result.len == original.len
17+
assert result.first() == original.last()
18+
assert result.reverse() == original
19+
eprintln('> original: ${original}')
20+
eprintln('> result: ${result}')
21+
}
22+
23+
fn test_reverse_iterator_basic() {
24+
check(['abc', 'def', 'ghi', 'jkl'])
25+
check([10, 20, 30, 40])
26+
check([
27+
Compound{'abc', 123, 444, {
28+
'aa': i16(12)
29+
'bb': 31
30+
}},
31+
Compound{'def', 456, 555, {
32+
'bb': i16(22)
33+
'cc': 32
34+
}},
35+
Compound{'xyz', 789, 666, {
36+
'cc': i16(32)
37+
'dd': 33
38+
}},
39+
])
40+
}
41+
42+
fn test_reverse_iterator_with_mut() {
43+
mut original := [10, 20]
44+
mut before := []int{cap: original.len}
45+
mut after := []int{cap: original.len}
46+
for mut x in arrays.reverse_iterator(original) {
47+
before << *x
48+
(**x)++
49+
after << *x
50+
}
51+
assert before == [20, 10]
52+
assert after == [21, 11]
53+
assert original == [11, 21]
54+
}
55+
56+
fn test_reverse_iterator_with_mut_compound() {
57+
mut original := [Compound{
58+
s: 'abc'
59+
i: 123
60+
}, Compound{
61+
s: 'xyz'
62+
i: 987
63+
}]
64+
mut before := []Compound{cap: original.len}
65+
mut after := []Compound{cap: original.len}
66+
for mut x in arrays.reverse_iterator(original) {
67+
before << *x
68+
x.i++
69+
x.s += ' tail'
70+
x.u = 99
71+
x.m['modified'] = 1
72+
after << *x
73+
}
74+
assert after[0] == Compound{
75+
s: 'xyz tail'
76+
i: 988
77+
u: 99
78+
m: {
79+
'modified': i16(1)
80+
}
81+
}
82+
assert before[0] == Compound{
83+
s: 'xyz'
84+
i: 987
85+
u: 0
86+
m: {}
87+
}
88+
assert after.reverse() == original
89+
}

0 commit comments

Comments
 (0)