diff --git a/vlib/datatypes/doubly_linked_list.v b/vlib/datatypes/doubly_linked_list.v index c6af7aa7bc..7a22c74015 100644 --- a/vlib/datatypes/doubly_linked_list.v +++ b/vlib/datatypes/doubly_linked_list.v @@ -7,6 +7,7 @@ mut: prev &DoublyListNode = unsafe { 0 } } +// DoublyLinkedList represents a generic doubly linked list of elements, each of type T. pub struct DoublyLinkedList { mut: head &DoublyListNode = unsafe { 0 } @@ -254,7 +255,7 @@ pub fn (list DoublyLinkedList) str() string { // array returns a array representation of the linked list pub fn (list DoublyLinkedList) 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) 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) next() ?T { if list.iter == unsafe { nil } { // initialize new iter object @@ -283,7 +284,58 @@ pub fn (mut list DoublyLinkedList) next() ?T { return list.iter.node.data } -struct DoublyListIter { +// iterator returns a new iterator instance for the `list`. +pub fn (mut list DoublyLinkedList) iterator() DoublyListIter { + return DoublyListIter{ + node: list.head + } +} + +// back_iterator returns a new backwards iterator instance for the `list`. +pub fn (mut list DoublyLinkedList) back_iterator() DoublyListIterBack { + return DoublyListIterBack{ + node: list.tail + } +} + +// DoublyListIter 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 { mut: node &DoublyListNode = 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) next() ?T { + if iter.node == unsafe { nil } { + return none + } + res := iter.node.data + iter.node = iter.node.next + return res +} + +// DoublyListIterBack 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 { +mut: + node &DoublyListNode = 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) next() ?T { + if iter.node == unsafe { nil } { + return none + } + res := iter.node.data + iter.node = iter.node.prev + return res +} diff --git a/vlib/datatypes/doubly_linked_list_test.v b/vlib/datatypes/doubly_linked_list_test.v index 74031d4350..07cbcc1180 100644 --- a/vlib/datatypes/doubly_linked_list_test.v +++ b/vlib/datatypes/doubly_linked_list_test.v @@ -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{} + 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{} + 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{} + 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] +} diff --git a/vlib/datatypes/linked_list.v b/vlib/datatypes/linked_list.v index 901f21abf9..63b4b24b50 100644 --- a/vlib/datatypes/linked_list.v +++ b/vlib/datatypes/linked_list.v @@ -10,6 +10,10 @@ pub struct LinkedList { mut: head &ListNode = 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 = unsafe { 0 } } // is_empty checks if the linked list is empty @@ -152,7 +156,7 @@ pub fn (list LinkedList) str() string { // array returns a array representation of the linked list pub fn (list LinkedList) 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) array() []T { } return result_array } + +// next implements the iteration interface to use LinkedList +// with V's `for` loop syntax. +pub fn (mut list LinkedList) next() ?T { + if list.iter == unsafe { nil } { + // initialize new iter object + list.iter = &ListIter{ + 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) iterator() ListIter { + return ListIter{ + node: list.head + } +} + +// ListIter 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 { +mut: + node &ListNode = 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) next() ?T { + if iter.node == unsafe { nil } { + return none + } + res := iter.node.data + iter.node = iter.node.next + return res +} diff --git a/vlib/datatypes/linked_list_test.v b/vlib/datatypes/linked_list_test.v index a37278a7b8..8bf1e07445 100644 --- a/vlib/datatypes/linked_list_test.v +++ b/vlib/datatypes/linked_list_test.v @@ -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{} + 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{} + 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] +}