mirror of
https://github.com/vlang/v.git
synced 2025-08-03 17:57:59 -04:00
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)
This commit is contained in:
parent
29188e9849
commit
f822792a10
37
vlib/arrays/reverse_iterator.v
Normal file
37
vlib/arrays/reverse_iterator.v
Normal file
@ -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.
|
||||
}
|
89
vlib/arrays/reverse_iterator_test.v
Normal file
89
vlib/arrays/reverse_iterator_test.v
Normal file
@ -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
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user