From e8c6922580d9f92caac1394052e2dfa5085e1b85 Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Sat, 15 Mar 2025 01:47:13 +0800 Subject: [PATCH] datatypes: optimize linkedlist (fix #23928) (#23934) --- vlib/datatypes/linked_list.v | 103 ++++++++++++++++-------------- vlib/datatypes/linked_list_test.v | 9 ++- 2 files changed, 62 insertions(+), 50 deletions(-) diff --git a/vlib/datatypes/linked_list.v b/vlib/datatypes/linked_list.v index 475d83955..e84f8182c 100644 --- a/vlib/datatypes/linked_list.v +++ b/vlib/datatypes/linked_list.v @@ -1,66 +1,61 @@ module datatypes +@[heap] pub struct ListNode[T] { mut: data T - next &ListNode[T] = unsafe { 0 } + next &ListNode[T] = unsafe { nil } } +@[heap] pub struct LinkedList[T] { mut: - head &ListNode[T] = unsafe { 0 } + head &ListNode[T] = unsafe { nil } + tail &ListNode[T] = unsafe { nil } 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 } + iter &ListIter[T] = unsafe { nil } } // is_empty checks if the linked list is empty +@[inline] pub fn (list LinkedList[T]) is_empty() bool { return list.len == 0 } // len returns the length of the linked list +@[inline] pub fn (list LinkedList[T]) len() int { return list.len } // first returns the first element of the linked list +@[inline] pub fn (list LinkedList[T]) first() !T { return if !list.is_empty() { list.head.data } else { error('Linked list is empty') } } // last returns the last element of the linked list +@[inline] pub fn (list LinkedList[T]) last() !T { - if unsafe { list.head == 0 } { - return error('Linked list is empty') - } else { - mut node := list.head - for unsafe { node.next != 0 } { - node = node.next - } - return node.data - } + return if !list.is_empty() { list.tail.data } else { error('Linked list is empty') } } // index returns the element at the given index of the linked list pub fn (list LinkedList[T]) index(idx int) !T { - if unsafe { list.head == 0 } { + if list.is_empty() { return error('Linked list is empty') - } else { - mut node := list.head - mut iterations := 0 - for unsafe { node.next != 0 } && iterations < idx { - node = node.next - iterations++ - } - if iterations == idx { - return node.data - } else { - return error('Index ${idx} != iterations: ${iterations}') - } } + if idx < 0 || idx >= list.len { + return error('Index ${idx} out of bounds') + } + mut node := list.head + for _ in 0 .. idx { + node = node.next + } + return node.data } // push adds an element to the end of the linked list @@ -68,16 +63,13 @@ pub fn (mut list LinkedList[T]) push(item T) { new_node := &ListNode[T]{ data: item } - if unsafe { list.head == 0 } { + if list.is_empty() { // first node case list.head = new_node } else { - mut node := list.head - for unsafe { node.next != 0 } { - node = node.next - } - node.next = new_node + list.tail.next = new_node } + list.tail = new_node list.len += 1 } @@ -90,22 +82,24 @@ pub fn (mut list LinkedList[T]) push_many(elements []T) { // pop removes the last element of the linked list pub fn (mut list LinkedList[T]) pop() !T { - if unsafe { list.head == 0 } { + if list.is_empty() { return error('Linked list is empty') } mut node := list.head mut to_return := unsafe { node.data } - if unsafe { node.next == 0 } { + if isnil(node.next) { // first node case // set to null list.head = unsafe { nil } + list.tail = unsafe { nil } } else { - for unsafe { node.next.next != 0 } { + for !isnil(node.next.next) { node = node.next } to_return = unsafe { node.next.data } // set to null node.next = unsafe { nil } + list.tail = node } list.len -= 1 return to_return @@ -113,12 +107,15 @@ pub fn (mut list LinkedList[T]) pop() !T { // shift removes the first element of the linked list pub fn (mut list LinkedList[T]) shift() !T { - if unsafe { list.head == 0 } { + if list.is_empty() { return error('Linked list is empty') } else { list.len -= 1 node := list.head list.head = node.next + if list.is_empty() { + list.tail = unsafe { nil } + } return node.data } } @@ -127,28 +124,36 @@ pub fn (mut list LinkedList[T]) shift() !T { pub fn (mut list LinkedList[T]) insert(idx int, item T) ! { if idx < 0 || idx > list.len { return error('Index ${idx} out of bounds [0..${list.len}]') - } else if list.len == 0 { + } + if idx == list.len { list.push(item) + return + } + mut new_node := &ListNode[T]{ + data: item + } + if list.is_empty() { + list.head = new_node + list.tail = new_node } else { - list.len += 1 mut node := list.head if idx == 0 { // first node case - list.head = &ListNode[T]{ - data: item - next: node - } + new_node.next = node + list.head = new_node } else { for i := 0; i < idx - 1; i++ { node = node.next } - node.next = &ListNode[T]{ - data: item - next: node.next + new_node.next = node.next + node.next = new_node + if isnil(new_node.next) { + list.tail = new_node } } } + list.len += 1 } // prepend adds an element to the beginning of the linked list (equivalent to insert(0, item)) @@ -165,7 +170,7 @@ pub fn (list LinkedList[T]) str() string { pub fn (list LinkedList[T]) array() []T { mut result_array := []T{cap: list.len} mut node := list.head - for unsafe { node != 0 } { + for !isnil(node) { result_array << node.data node = node.next } @@ -175,14 +180,14 @@ pub fn (list LinkedList[T]) array() []T { // 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 } { + if isnil(list.iter) { // initialize new iter object list.iter = &ListIter[T]{ node: list.head } return list.next() } - if list.iter.node == unsafe { nil } { + if isnil(list.iter.node) { list.iter = unsafe { nil } return none } @@ -205,13 +210,13 @@ pub fn (mut list LinkedList[T]) iterator() ListIter[T] { // An iterator instance always traverses the list from start to finish. pub struct ListIter[T] { mut: - node &ListNode[T] = unsafe { 0 } + node &ListNode[T] = unsafe { nil } } // 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 } { + if isnil(iter.node) { return none } res := unsafe { iter.node.data } diff --git a/vlib/datatypes/linked_list_test.v b/vlib/datatypes/linked_list_test.v index 25ae4ebe3..ff24f42ea 100644 --- a/vlib/datatypes/linked_list_test.v +++ b/vlib/datatypes/linked_list_test.v @@ -32,7 +32,6 @@ fn test_push_many() { x := [1, 2, 3, 4, 5] list.push_many(x) assert list.first()! == 1 - assert list.first()! == 1 list = LinkedList[int]{} list.first() or { return } assert false @@ -41,6 +40,7 @@ fn test_push_many() { fn test_last() { mut list := LinkedList[int]{} list.push(1) + assert list.first()! == 1 assert list.last()! == 1 list.push(2) assert list.last()! == 2 @@ -56,6 +56,7 @@ fn test_index() { list.push(2) assert list.index(1)! == 2 list.pop()! + assert list.index(0)! == 1 list.index(1) or { return } assert false } @@ -104,6 +105,11 @@ fn test_insert() { list.push(2) list.push(3) list.insert(1, 111) or { return } + assert list.index(0)! == 1 + assert list.index(1)! == 111 + assert list.index(2)! == 2 + list.insert(4, 222) or { return } + assert list.last()! == 222 assert true } @@ -114,6 +120,7 @@ fn test_prepend() { list.push(3) list.prepend(111) assert list.first()! == 111 + assert list.index(1)! == 1 } fn test_str() { -- 2.39.5