1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

datatypes: add a forward iterator for LinkedList<T>, add forward and backward iterators for DoublyLinkedList<T>. Add tests for both.

This commit is contained in:
Delyan Angelov 2022-09-23 22:29:13 +03:00
parent a6576bec1d
commit 1f26e3fb1b
4 changed files with 201 additions and 4 deletions

View File

@ -7,6 +7,7 @@ mut:
prev &DoublyListNode<T> = unsafe { 0 }
}
// DoublyLinkedList<T> represents a generic doubly linked list of elements, each of type T.
pub struct DoublyLinkedList<T> {
mut:
head &DoublyListNode<T> = unsafe { 0 }
@ -254,7 +255,7 @@ pub fn (list DoublyLinkedList<T>) str() string {
// array returns a array representation of the linked list
pub fn (list DoublyLinkedList<T>) array() []T {
mut result_array := []T{}
mut result_array := []T{cap: list.len}
mut node := list.head
for unsafe { node != 0 } {
result_array << node.data
@ -264,7 +265,7 @@ pub fn (list DoublyLinkedList<T>) array() []T {
}
// next implements the iter interface to use DoublyLinkedList with
// V's for loop syntax.
// V's `for x in list {` loop syntax.
pub fn (mut list DoublyLinkedList<T>) next() ?T {
if list.iter == unsafe { nil } {
// initialize new iter object
@ -283,7 +284,58 @@ pub fn (mut list DoublyLinkedList<T>) next() ?T {
return list.iter.node.data
}
struct DoublyListIter<T> {
// iterator returns a new iterator instance for the `list`.
pub fn (mut list DoublyLinkedList<T>) iterator() DoublyListIter<T> {
return DoublyListIter<T>{
node: list.head
}
}
// back_iterator returns a new backwards iterator instance for the `list`.
pub fn (mut list DoublyLinkedList<T>) back_iterator() DoublyListIterBack<T> {
return DoublyListIterBack<T>{
node: list.tail
}
}
// DoublyListIter<T> is an iterator for DoublyLinkedList.
// It starts from *the start* and moves forwards to *the end* of the list.
// It can be used with V's `for x in iter {` construct.
// One list can have multiple independent iterators, pointing to different positions/places in the list.
// A DoublyListIter iterator instance always traverses the list from *start to finish*.
pub struct DoublyListIter<T> {
mut:
node &DoublyListNode<T> = unsafe { 0 }
}
// next returns *the next* element of the list, or `none` when the end of the list is reached.
// It is called by V's `for x in iter{` on each iteration.
pub fn (mut iter DoublyListIter<T>) next() ?T {
if iter.node == unsafe { nil } {
return none
}
res := iter.node.data
iter.node = iter.node.next
return res
}
// DoublyListIterBack<T> is an iterator for DoublyLinkedList.
// It starts from *the end* and moves backwards to *the start* of the list.
// It can be used with V's `for x in iter {` construct.
// One list can have multiple independent iterators, pointing to different positions/places in the list.
// A DoublyListIterBack iterator instance always traverses the list from *finish to start*.
pub struct DoublyListIterBack<T> {
mut:
node &DoublyListNode<T> = unsafe { 0 }
}
// next returns *the previous* element of the list, or `none` when the start of the list is reached.
// It is called by V's `for x in iter{` on each iteration.
pub fn (mut iter DoublyListIterBack<T>) next() ?T {
if iter.node == unsafe { nil } {
return none
}
res := iter.node.data
iter.node = iter.node.prev
return res
}

View File

@ -166,3 +166,49 @@ fn test_array() ? {
list.push_back(3)
assert list.array() == [1, 2, 3]
}
fn test_string_array() ? {
mut list := DoublyLinkedList<[]string>{}
list.push_back(['a'])
list.push_back(['b'])
list.push_back(['c'])
assert list.array() == [['a'], ['b'], ['c']]
}
fn test_iteration_with_for() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(1)
list.push_back(2)
list.push_back(3)
mut res := []int{}
for x in list {
res << x
}
assert res == [1, 2, 3]
}
fn test_iterator() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(1)
list.push_back(2)
list.push_back(3)
mut iter := list.iterator()
mut res := []int{}
for x in iter {
res << x
}
assert res == [1, 2, 3]
}
fn test_back_iterator() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(1)
list.push_back(2)
list.push_back(3)
mut iter := list.back_iterator()
mut res := []int{}
for x in iter {
res << x
}
assert res == [3, 2, 1]
}

View File

@ -10,6 +10,10 @@ pub struct LinkedList<T> {
mut:
head &ListNode<T> = unsafe { 0 }
len int
// Internal iter pointer for allowing safe modification
// of the list while iterating. TODO: use an option
// instead of a pointer to determine if it is initialized.
iter &ListIter<T> = unsafe { 0 }
}
// is_empty checks if the linked list is empty
@ -152,7 +156,7 @@ pub fn (list LinkedList<T>) str() string {
// array returns a array representation of the linked list
pub fn (list LinkedList<T>) array() []T {
mut result_array := []T{}
mut result_array := []T{cap: list.len}
mut node := list.head
for unsafe { node != 0 } {
result_array << node.data
@ -160,3 +164,50 @@ pub fn (list LinkedList<T>) array() []T {
}
return result_array
}
// next implements the iteration interface to use LinkedList
// with V's `for` loop syntax.
pub fn (mut list LinkedList<T>) next() ?T {
if list.iter == unsafe { nil } {
// initialize new iter object
list.iter = &ListIter<T>{
node: list.head
}
return list.next()
}
if list.iter.node == unsafe { nil } {
list.iter = unsafe { nil }
return none
}
defer {
list.iter.node = list.iter.node.next
}
return list.iter.node.data
}
// iterator returns a new iterator instance for the `list`.
pub fn (mut list LinkedList<T>) iterator() ListIter<T> {
return ListIter<T>{
node: list.head
}
}
// ListIter<T> is an iterator for LinkedList.
// It can be used with V's `for x in iter {` construct.
// One list can have multiple independent iterators, pointing to different positions/places in the list.
// An iterator instance always traverses the list from start to finish.
pub struct ListIter<T> {
mut:
node &ListNode<T> = unsafe { 0 }
}
// next returns the next element of the list, or `none` when the end of the list is reached.
// It is called by V's `for x in iter{` on each iteration.
pub fn (mut iter ListIter<T>) next() ?T {
if iter.node == unsafe { nil } {
return none
}
res := iter.node.data
iter.node = iter.node.next
return res
}

View File

@ -120,3 +120,51 @@ fn test_array() ? {
list.push(3)
assert list.array() == [1, 2, 3]
}
fn test_linked_list_iterating_with_for() ? {
mut list := LinkedList<int>{}
list.push(1)
list.push(2)
list.push(3)
mut res := []int{}
for x in list {
res << x
}
assert res == [1, 2, 3]
}
fn test_linked_list_separate_iterators() ? {
mut list := LinkedList<int>{}
list.push(1)
list.push(2)
list.push(3)
mut it1 := list.iterator()
mut it2 := list.iterator()
mut it3 := list.iterator()
assert it1.next()? == 1
assert it1.next()? == 2
assert it1.next()? == 3
assert it2.next()? == 1
if _ := it1.next() {
assert false
} else {
assert true
}
if _ := it1.next() {
assert false
} else {
assert true
}
assert it2.next()? == 2
assert it2.next()? == 3
if _ := it2.next() {
assert false
} else {
assert true
}
mut res := []int{}
for x in it3 {
res << x
}
assert res == [1, 2, 3]
}