mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
vlib: rename adt
to datatypes
This commit is contained in:
29
vlib/datatypes/README.md
Normal file
29
vlib/datatypes/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
# datatypes
|
||||
|
||||
This module provides implementations of less frequently used, but still common
|
||||
data types.
|
||||
|
||||
V's `builtin` module is imported implicitly, and has implementations for arrays,
|
||||
maps and strings. These are good for many applications, but there are a plethora
|
||||
of other useful data structures/containers, like linked lists, priority queues,
|
||||
tries, etc, that allow for algorithms with different time complexities, which may
|
||||
be more suitable for your specific application.
|
||||
|
||||
It is implemented using generics, that you have to specialise for the type of
|
||||
your actual elements. For example:
|
||||
```v
|
||||
import datatypes
|
||||
|
||||
mut stack := datatypes.Stack<int>{}
|
||||
stack.push(1)
|
||||
println(stack)
|
||||
```
|
||||
|
||||
## Currently Implemented Datatypes:
|
||||
|
||||
- [x] Linked list
|
||||
- [x] Doubly linked list
|
||||
- [x] Stack (LIFO)
|
||||
- [x] Queue (FIFO)
|
||||
- [x] Min heap (priority queue)
|
||||
- [ ] ...
|
284
vlib/datatypes/doubly_linked_list.v
Normal file
284
vlib/datatypes/doubly_linked_list.v
Normal file
@ -0,0 +1,284 @@
|
||||
module datatypes
|
||||
|
||||
struct DoublyListNode<T> {
|
||||
mut:
|
||||
data T
|
||||
next &DoublyListNode<T> = 0
|
||||
prev &DoublyListNode<T> = 0
|
||||
}
|
||||
|
||||
pub struct DoublyLinkedList<T> {
|
||||
mut:
|
||||
head &DoublyListNode<T> = 0
|
||||
tail &DoublyListNode<T> = 0
|
||||
// Internal iter pointer for allowing safe modification
|
||||
// of the list while iterating. TODO: use an option
|
||||
// instead of a pointer to determine it is initialized.
|
||||
iter &DoublyListIter<T> = 0
|
||||
len int
|
||||
}
|
||||
|
||||
// is_empty checks if the linked list is empty
|
||||
pub fn (list DoublyLinkedList<T>) is_empty() bool {
|
||||
return list.len == 0
|
||||
}
|
||||
|
||||
// len returns the length of the linked list
|
||||
pub fn (list DoublyLinkedList<T>) len() int {
|
||||
return list.len
|
||||
}
|
||||
|
||||
// first returns the first element of the linked list
|
||||
pub fn (list DoublyLinkedList<T>) first() ?T {
|
||||
if list.is_empty() {
|
||||
return error('Linked list is empty')
|
||||
}
|
||||
return list.head.data
|
||||
}
|
||||
|
||||
// last returns the last element of the linked list
|
||||
pub fn (list DoublyLinkedList<T>) last() ?T {
|
||||
if list.is_empty() {
|
||||
return error('Linked list is empty')
|
||||
}
|
||||
return list.tail.data
|
||||
}
|
||||
|
||||
// push_back adds an element to the end of the linked list
|
||||
pub fn (mut list DoublyLinkedList<T>) push_back(item T) {
|
||||
mut new_node := &DoublyListNode{
|
||||
data: item
|
||||
}
|
||||
if list.is_empty() {
|
||||
// first node case
|
||||
list.head = new_node
|
||||
list.tail = new_node
|
||||
} else {
|
||||
list.tail.next = new_node
|
||||
new_node.prev = list.tail
|
||||
list.tail = new_node
|
||||
}
|
||||
list.len += 1
|
||||
}
|
||||
|
||||
// push_front adds an element to the beginning of the linked list
|
||||
pub fn (mut list DoublyLinkedList<T>) push_front(item T) {
|
||||
mut new_node := &DoublyListNode{
|
||||
data: item
|
||||
}
|
||||
if list.is_empty() {
|
||||
// first node case
|
||||
list.head = new_node
|
||||
list.tail = new_node
|
||||
} else {
|
||||
list.head.prev = new_node
|
||||
new_node.next = list.head
|
||||
list.head = new_node
|
||||
}
|
||||
list.len += 1
|
||||
}
|
||||
|
||||
// pop_back removes the last element of the linked list
|
||||
pub fn (mut list DoublyLinkedList<T>) pop_back() ?T {
|
||||
if list.is_empty() {
|
||||
return error('Linked list is empty')
|
||||
}
|
||||
defer {
|
||||
list.len -= 1
|
||||
}
|
||||
if list.len == 1 {
|
||||
// head == tail
|
||||
value := list.tail.data
|
||||
list.head = voidptr(0)
|
||||
list.tail = voidptr(0)
|
||||
return value
|
||||
}
|
||||
value := list.tail.data
|
||||
list.tail.prev.next = voidptr(0) // unlink tail
|
||||
list.tail = list.tail.prev
|
||||
return value
|
||||
}
|
||||
|
||||
// pop_front removes the last element of the linked list
|
||||
pub fn (mut list DoublyLinkedList<T>) pop_front() ?T {
|
||||
if list.is_empty() {
|
||||
return error('Linked list is empty')
|
||||
}
|
||||
defer {
|
||||
list.len -= 1
|
||||
}
|
||||
if list.len == 1 {
|
||||
// head == tail
|
||||
value := list.head.data
|
||||
list.head = voidptr(0)
|
||||
list.tail = voidptr(0)
|
||||
return value
|
||||
}
|
||||
value := list.head.data
|
||||
list.head.next.prev = voidptr(0) // unlink head
|
||||
list.head = list.head.next
|
||||
return value
|
||||
}
|
||||
|
||||
// insert adds an element to the linked list at the given index
|
||||
pub fn (mut list DoublyLinkedList<T>) insert(idx int, item T) ? {
|
||||
if idx < 0 || idx > list.len {
|
||||
return error('Index out of bounds')
|
||||
} else if idx == 0 {
|
||||
// new head
|
||||
list.push_front(item)
|
||||
} else if idx == list.len {
|
||||
// new tail
|
||||
list.push_back(item)
|
||||
} else if idx <= list.len / 2 {
|
||||
list.insert_front(idx, item)
|
||||
} else {
|
||||
list.insert_back(idx, item)
|
||||
}
|
||||
}
|
||||
|
||||
// insert_back walks from the tail and inserts a new item at index idx
|
||||
// (determined from the forward index). This function should be called
|
||||
// when idx > list.len/2. This helper function assumes idx bounds have
|
||||
// already been checked and idx is not at the edges.
|
||||
fn (mut list DoublyLinkedList<T>) insert_back(idx int, item T) {
|
||||
mut node := list.node(idx + 1)
|
||||
mut prev := node.prev
|
||||
// prev node
|
||||
// ------ ------
|
||||
// |next|---->|next|
|
||||
// |prev|<----|prev|
|
||||
// ------ ------
|
||||
new := &DoublyListNode{
|
||||
data: item
|
||||
next: node
|
||||
prev: prev
|
||||
}
|
||||
// prev new node
|
||||
// ------ ------ ------
|
||||
// |next|---->|next|---->|next|
|
||||
// |prev|<----|prev|<----|prev|
|
||||
// ------ ------ ------
|
||||
node.prev = new
|
||||
prev.next = new
|
||||
list.len += 1
|
||||
}
|
||||
|
||||
// insert_front walks from the head and inserts a new item at index idx
|
||||
// (determined from the forward index). This function should be called
|
||||
// when idx <= list.len/2. This helper function assumes idx bounds have
|
||||
// already been checked and idx is not at the edges.
|
||||
fn (mut list DoublyLinkedList<T>) insert_front(idx int, item T) {
|
||||
mut node := list.node(idx - 1)
|
||||
mut next := node.next
|
||||
// node next
|
||||
// ------ ------
|
||||
// |next|---->|next|
|
||||
// |prev|<----|prev|
|
||||
// ------ ------
|
||||
new := &DoublyListNode{
|
||||
data: item
|
||||
next: next
|
||||
prev: node
|
||||
}
|
||||
// node new next
|
||||
// ------ ------ ------
|
||||
// |next|---->|next|---->|next|
|
||||
// |prev|<----|prev|<----|prev|
|
||||
// ------ ------ ------
|
||||
node.next = new
|
||||
next.prev = new
|
||||
list.len += 1
|
||||
}
|
||||
|
||||
// node walks from the head or tail and finds the node at index idx.
|
||||
// This helper function assumes the list is not empty and idx is in
|
||||
// bounds.
|
||||
fn (list &DoublyLinkedList<T>) node(idx int) &DoublyListNode<T> {
|
||||
if idx <= list.len / 2 {
|
||||
mut node := list.head
|
||||
for h := 0; h < idx; h += 1 {
|
||||
node = node.next
|
||||
}
|
||||
return node
|
||||
}
|
||||
mut node := list.tail
|
||||
for t := list.len - 1; t >= idx; t -= 1 {
|
||||
node = node.prev
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// index searches the linked list for item and returns the forward index
|
||||
// or none if not found.
|
||||
pub fn (list &DoublyLinkedList<T>) index(item T) ?int {
|
||||
mut hn := list.head
|
||||
mut tn := list.tail
|
||||
for h, t := 0, list.len - 1; h <= t; {
|
||||
if hn.data == item {
|
||||
return h
|
||||
} else if tn.data == item {
|
||||
return t
|
||||
}
|
||||
h += 1
|
||||
hn = hn.next
|
||||
t -= 1
|
||||
tn = tn.prev
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// delete removes index idx from the linked list and is safe to call
|
||||
// for any idx.
|
||||
pub fn (mut list DoublyLinkedList<T>) delete(idx int) {
|
||||
if idx < 0 || idx >= list.len {
|
||||
return
|
||||
} else if idx == 0 {
|
||||
list.pop_front() or {}
|
||||
return
|
||||
} else if idx == list.len - 1 {
|
||||
list.pop_back() or {}
|
||||
return
|
||||
}
|
||||
// node should be somewhere in the middle
|
||||
mut node := list.node(idx)
|
||||
node.prev.next = node.next
|
||||
node.next.prev = node.prev
|
||||
list.len -= 1
|
||||
}
|
||||
|
||||
// str returns a string representation of the linked list
|
||||
pub fn (list DoublyLinkedList<T>) str() string {
|
||||
mut result_array := []T{}
|
||||
mut node := list.head
|
||||
for node != 0 {
|
||||
result_array << node.data
|
||||
node = node.next
|
||||
}
|
||||
return result_array.str()
|
||||
}
|
||||
|
||||
// next implements the iter interface to use DoublyLinkedList with
|
||||
// V's for loop syntax.
|
||||
pub fn (mut list DoublyLinkedList<T>) next() ?T {
|
||||
if list.iter == voidptr(0) {
|
||||
// initialize new iter object
|
||||
list.iter = &DoublyListIter<T>{
|
||||
node: list.head
|
||||
}
|
||||
return list.next()
|
||||
}
|
||||
if list.iter.node == voidptr(0) {
|
||||
list.iter = voidptr(0)
|
||||
return none
|
||||
}
|
||||
defer {
|
||||
list.iter.node = list.iter.node.next
|
||||
}
|
||||
return list.iter.node.data
|
||||
}
|
||||
|
||||
struct DoublyListIter<T> {
|
||||
mut:
|
||||
node &DoublyListNode<T> = 0
|
||||
}
|
160
vlib/datatypes/doubly_linked_list_test.v
Normal file
160
vlib/datatypes/doubly_linked_list_test.v
Normal file
@ -0,0 +1,160 @@
|
||||
module datatypes
|
||||
|
||||
fn test_is_empty() {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
assert list.is_empty() == true
|
||||
list.push_back(1)
|
||||
assert list.is_empty() == false
|
||||
}
|
||||
|
||||
fn test_len() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
assert list.len() == 0
|
||||
list.push_back(1)
|
||||
assert list.len() == 1
|
||||
list.pop_back() ?
|
||||
assert list.len() == 0
|
||||
}
|
||||
|
||||
fn test_first() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
list.push_back(1)
|
||||
assert list.first() ? == 1
|
||||
list.push_back(2)
|
||||
assert list.first() ? == 1
|
||||
list = DoublyLinkedList<int>{}
|
||||
list.first() or { return }
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_last() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
list.push_back(1)
|
||||
assert list.last() ? == 1
|
||||
list.push_back(2)
|
||||
assert list.last() ? == 2
|
||||
list = DoublyLinkedList<int>{}
|
||||
list.last() or { return }
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_push() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
list.push_back(1)
|
||||
assert list.last() ? == 1
|
||||
list.push_back(2)
|
||||
assert list.last() ? == 2
|
||||
list.push_back(3)
|
||||
assert list.last() ? == 3
|
||||
}
|
||||
|
||||
fn test_pop() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
list.push_back(1)
|
||||
list.push_back(2)
|
||||
list.push_back(3)
|
||||
assert list.pop_back() ? == 3
|
||||
list.push_back(4)
|
||||
assert list.pop_back() ? == 4
|
||||
assert list.pop_back() ? == 2
|
||||
list = DoublyLinkedList<int>{}
|
||||
list.pop_back() or { return }
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_pop_front() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
list.push_back(1)
|
||||
list.push_back(2)
|
||||
list.push_back(3)
|
||||
assert list.pop_front() ? == 1
|
||||
list.push_back(4)
|
||||
assert list.pop_front() ? == 2
|
||||
assert list.pop_front() ? == 3
|
||||
list = DoublyLinkedList<int>{}
|
||||
list.pop_front() or { return }
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_insert() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
list.push_back(1)
|
||||
list.push_back(2)
|
||||
list.push_back(3)
|
||||
// [1, 2, 3]
|
||||
list.insert(1, 111) ?
|
||||
// [1, 111, 2, 3]
|
||||
list.insert(3, 222) ?
|
||||
// [1, 111, 2, 222, 3]
|
||||
assert list.pop_back() ? == 3
|
||||
assert list.pop_back() ? == 222
|
||||
assert list.pop_front() ? == 1
|
||||
assert list.pop_front() ? == 111
|
||||
}
|
||||
|
||||
fn test_push_front() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
list.push_back(1)
|
||||
list.push_back(2)
|
||||
list.push_back(3)
|
||||
list.push_front(111)
|
||||
assert list.first() ? == 111
|
||||
}
|
||||
|
||||
fn test_delete() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
list.push_back(0)
|
||||
list.push_back(1)
|
||||
list.push_back(2)
|
||||
list.delete(1)
|
||||
assert list.first() ? == 0
|
||||
assert list.last() ? == 2
|
||||
assert list.len() == 2
|
||||
list.delete(1)
|
||||
assert list.first() ? == 0
|
||||
assert list.last() ? == 0
|
||||
assert list.len() == 1
|
||||
list.delete(0)
|
||||
assert list.len() == 0
|
||||
}
|
||||
|
||||
fn test_iter() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
for i := 0; i < 10; i++ {
|
||||
list.push_back(i * 10)
|
||||
}
|
||||
|
||||
mut count := 0
|
||||
for i, v in list {
|
||||
count += 1
|
||||
assert int(i * 10) == v
|
||||
}
|
||||
assert count == 10
|
||||
|
||||
// test it gets reset
|
||||
count = 0
|
||||
for i, v in list {
|
||||
count += 1
|
||||
assert int(i * 10) == v
|
||||
}
|
||||
assert count == 10
|
||||
}
|
||||
|
||||
fn test_index() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
for i := 0; i < 10; i++ {
|
||||
list.push_back(i * 10)
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
assert list.index(i * 10) ? == i
|
||||
}
|
||||
}
|
||||
|
||||
fn test_str() ? {
|
||||
mut list := DoublyLinkedList<int>{}
|
||||
list.push_back(1)
|
||||
list.push_back(2)
|
||||
list.push_back(3)
|
||||
assert list.str() == '[1, 2, 3]'
|
||||
}
|
84
vlib/datatypes/heap.v
Normal file
84
vlib/datatypes/heap.v
Normal file
@ -0,0 +1,84 @@
|
||||
module datatypes
|
||||
|
||||
// MinHeap is a binary minimum heap data structure.
|
||||
pub struct MinHeap<T> {
|
||||
mut:
|
||||
data []T
|
||||
}
|
||||
|
||||
// insert adds an element to the heap.
|
||||
pub fn (mut heap MinHeap<T>) insert(item T) {
|
||||
// push item to the end of the array
|
||||
heap.data << item
|
||||
// swap the new node with its parent until the heap is in order
|
||||
mut child := heap.data.len - 1
|
||||
mut parent := heap.parent(child)
|
||||
for heap.data[parent] > heap.data[child] {
|
||||
heap.data[parent], heap.data[child] = heap.data[child], heap.data[parent]
|
||||
child = parent
|
||||
parent = heap.parent(child)
|
||||
}
|
||||
}
|
||||
|
||||
// pop removes the top-most element from the heap.
|
||||
pub fn (mut heap MinHeap<T>) pop() ?T {
|
||||
if heap.data.len == 0 {
|
||||
return none
|
||||
} else if heap.data.len == 1 {
|
||||
return heap.data.pop()
|
||||
}
|
||||
item := heap.data[0]
|
||||
// move last element to root
|
||||
heap.data[0] = heap.data.pop()
|
||||
// swap the new root with its minimum child until the heap is in order
|
||||
mut parent := 0
|
||||
mut left := heap.left_child(parent) or { return item }
|
||||
mut right := heap.right_child(parent) or { left }
|
||||
for heap.data[parent] > heap.data[left] || heap.data[parent] > heap.data[right] {
|
||||
// choose min for min heap
|
||||
swap := if heap.data[left] <= heap.data[right] { left } else { right }
|
||||
heap.data[parent], heap.data[swap] = heap.data[swap], heap.data[parent]
|
||||
parent = swap
|
||||
left = heap.left_child(parent) or { break }
|
||||
right = heap.right_child(parent) or { left }
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
// peek gets the top-most element from the heap without removing it.
|
||||
pub fn (heap MinHeap<T>) peek() ?T {
|
||||
if heap.data.len == 0 {
|
||||
return none
|
||||
}
|
||||
return heap.data[0]
|
||||
}
|
||||
|
||||
// len returns the number of elements in the heap.
|
||||
pub fn (heap MinHeap<T>) len() int {
|
||||
return heap.data.len
|
||||
}
|
||||
|
||||
// left_child is a helper function that returns the index of the left
|
||||
// child given a parent idx, or none if there is no left child.
|
||||
fn (heap MinHeap<T>) left_child(idx int) ?int {
|
||||
child := 2 * idx + 1
|
||||
if child >= heap.data.len {
|
||||
return none
|
||||
}
|
||||
return child
|
||||
}
|
||||
|
||||
// right_child is a helper function that returns the index of the right
|
||||
// child given a parent idx, or none if there is no right child.
|
||||
fn (heap MinHeap<T>) right_child(idx int) ?int {
|
||||
child := 2 * idx + 2
|
||||
if child >= heap.data.len {
|
||||
return none
|
||||
}
|
||||
return child
|
||||
}
|
||||
|
||||
// parent is a helper function that returns the parent index of the child.
|
||||
fn (heap MinHeap<T>) parent(idx int) int {
|
||||
return (idx - 1) / 2
|
||||
}
|
68
vlib/datatypes/heap_test.v
Normal file
68
vlib/datatypes/heap_test.v
Normal file
@ -0,0 +1,68 @@
|
||||
module datatypes
|
||||
|
||||
fn test_min_heap() ? {
|
||||
mut heap := MinHeap<int>{}
|
||||
heap.insert(2)
|
||||
heap.insert(0)
|
||||
heap.insert(8)
|
||||
heap.insert(4)
|
||||
heap.insert(1)
|
||||
|
||||
assert heap.pop() ? == 0
|
||||
assert heap.pop() ? == 1
|
||||
assert heap.pop() ? == 2
|
||||
assert heap.pop() ? == 4
|
||||
assert heap.pop() ? == 8
|
||||
if _ := heap.pop() {
|
||||
panic('expected none')
|
||||
}
|
||||
}
|
||||
|
||||
struct Item {
|
||||
data string
|
||||
priority int
|
||||
}
|
||||
|
||||
fn (lhs Item) < (rhs Item) bool {
|
||||
return rhs.priority < lhs.priority
|
||||
}
|
||||
|
||||
fn test_min_heap_custom() ? {
|
||||
mut heap := MinHeap<Item>{}
|
||||
heap.insert(Item{'buz', 10})
|
||||
heap.insert(Item{'qux', 0})
|
||||
heap.insert(Item{'baz', 50})
|
||||
heap.insert(Item{'foo', 100})
|
||||
heap.insert(Item{'bar', 80})
|
||||
|
||||
assert heap.pop() ?.data == 'foo'
|
||||
assert heap.pop() ?.data == 'bar'
|
||||
assert heap.pop() ?.data == 'baz'
|
||||
assert heap.pop() ?.data == 'buz'
|
||||
assert heap.pop() ?.data == 'qux'
|
||||
if _ := heap.pop() {
|
||||
panic('expected none')
|
||||
}
|
||||
}
|
||||
|
||||
fn test_heap_len() ? {
|
||||
mut heap := MinHeap<int>{}
|
||||
heap.insert(2)
|
||||
assert heap.len() == 1
|
||||
heap.insert(0)
|
||||
heap.insert(8)
|
||||
heap.insert(4)
|
||||
assert heap.len() == 4
|
||||
heap.insert(1)
|
||||
|
||||
assert heap.len() == 5
|
||||
heap.pop() ?
|
||||
heap.pop() ?
|
||||
heap.pop() ?
|
||||
assert heap.len() == 2
|
||||
heap.pop() ?
|
||||
heap.pop() ?
|
||||
assert heap.len() == 0
|
||||
heap.pop() or {}
|
||||
assert heap.len() == 0
|
||||
}
|
138
vlib/datatypes/linked_list.v
Normal file
138
vlib/datatypes/linked_list.v
Normal file
@ -0,0 +1,138 @@
|
||||
module datatypes
|
||||
|
||||
pub struct ListNode<T> {
|
||||
mut:
|
||||
data T
|
||||
next &ListNode<T> = 0
|
||||
}
|
||||
|
||||
pub struct LinkedList<T> {
|
||||
mut:
|
||||
head &ListNode<T> = 0
|
||||
len int
|
||||
}
|
||||
|
||||
// is_empty checks if the linked list is empty
|
||||
pub fn (list LinkedList<T>) is_empty() bool {
|
||||
return list.len == 0
|
||||
}
|
||||
|
||||
// len returns the length of the linked list
|
||||
pub fn (list LinkedList<T>) len() int {
|
||||
return list.len
|
||||
}
|
||||
|
||||
// first returns the first element of the linked list
|
||||
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
|
||||
pub fn (list LinkedList<T>) last() ?T {
|
||||
if list.head == 0 {
|
||||
return error('Linked list is empty')
|
||||
} else {
|
||||
mut node := list.head
|
||||
for node.next != 0 {
|
||||
node = node.next
|
||||
}
|
||||
return node.data
|
||||
}
|
||||
}
|
||||
|
||||
// push adds an element to the end of the linked list
|
||||
pub fn (mut list LinkedList<T>) push(item T) {
|
||||
new_node := &ListNode{
|
||||
data: item
|
||||
}
|
||||
if list.head == 0 {
|
||||
// first node case
|
||||
list.head = new_node
|
||||
} else {
|
||||
mut node := list.head
|
||||
for node.next != 0 {
|
||||
node = node.next
|
||||
}
|
||||
node.next = new_node
|
||||
}
|
||||
list.len += 1
|
||||
}
|
||||
|
||||
// pop removes the last element of the linked list
|
||||
pub fn (mut list LinkedList<T>) pop() ?T {
|
||||
if list.head == 0 {
|
||||
return error('Linked list is empty')
|
||||
}
|
||||
mut node := list.head
|
||||
mut to_return := node.data
|
||||
if node.next == 0 {
|
||||
// first node case
|
||||
// set to null
|
||||
list.head = voidptr(0)
|
||||
} else {
|
||||
for node.next.next != 0 {
|
||||
node = node.next
|
||||
}
|
||||
to_return = node.next.data
|
||||
// set to null
|
||||
node.next = voidptr(0)
|
||||
}
|
||||
list.len -= 1
|
||||
return to_return
|
||||
}
|
||||
|
||||
// shift removes the first element of the linked list
|
||||
pub fn (mut list LinkedList<T>) shift() ?T {
|
||||
if list.head == 0 {
|
||||
return error('Linked list is empty')
|
||||
} else {
|
||||
list.len -= 1
|
||||
node := list.head
|
||||
list.head = node.next
|
||||
return node.data
|
||||
}
|
||||
}
|
||||
|
||||
// insert adds an element to the linked list at the given index
|
||||
pub fn (mut list LinkedList<T>) insert(idx int, item T) ? {
|
||||
if idx < 0 || idx > list.len {
|
||||
return error('Index out of bounds')
|
||||
} else if list.len == 0 {
|
||||
list.push(item)
|
||||
} else {
|
||||
list.len += 1
|
||||
mut node := list.head
|
||||
|
||||
if idx == 0 {
|
||||
// first node case
|
||||
list.head = &ListNode{
|
||||
data: item
|
||||
next: node
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < idx - 1; i++ {
|
||||
node = node.next
|
||||
}
|
||||
node.next = &ListNode{
|
||||
data: item
|
||||
next: node.next
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prepend adds an element to the beginning of the linked list (equivalent to insert(0, item))
|
||||
pub fn (mut list LinkedList<T>) prepend(item T) {
|
||||
list.insert(0, item) or {}
|
||||
}
|
||||
|
||||
// str returns a string representation of the linked list
|
||||
pub fn (list LinkedList<T>) str() string {
|
||||
mut result_array := []T{}
|
||||
mut node := list.head
|
||||
for node != 0 {
|
||||
result_array << node.data
|
||||
node = node.next
|
||||
}
|
||||
return result_array.str()
|
||||
}
|
103
vlib/datatypes/linked_list_test.v
Normal file
103
vlib/datatypes/linked_list_test.v
Normal file
@ -0,0 +1,103 @@
|
||||
module datatypes
|
||||
|
||||
fn test_is_empty() {
|
||||
mut list := LinkedList<int>{}
|
||||
assert list.is_empty() == true
|
||||
list.push(1)
|
||||
assert list.is_empty() == false
|
||||
}
|
||||
|
||||
fn test_len() ? {
|
||||
mut list := LinkedList<int>{}
|
||||
assert list.len() == 0
|
||||
list.push(1)
|
||||
assert list.len() == 1
|
||||
list.pop() ?
|
||||
assert list.len() == 0
|
||||
}
|
||||
|
||||
fn test_first() ? {
|
||||
mut list := LinkedList<int>{}
|
||||
list.push(1)
|
||||
assert list.first() ? == 1
|
||||
list.push(2)
|
||||
assert list.first() ? == 1
|
||||
list = LinkedList<int>{}
|
||||
list.first() or { return }
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_last() ? {
|
||||
mut list := LinkedList<int>{}
|
||||
list.push(1)
|
||||
assert list.last() ? == 1
|
||||
list.push(2)
|
||||
assert list.last() ? == 2
|
||||
list = LinkedList<int>{}
|
||||
list.last() or { return }
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_push() ? {
|
||||
mut list := LinkedList<int>{}
|
||||
list.push(1)
|
||||
assert list.last() ? == 1
|
||||
list.push(2)
|
||||
assert list.last() ? == 2
|
||||
list.push(3)
|
||||
assert list.last() ? == 3
|
||||
}
|
||||
|
||||
fn test_pop() ? {
|
||||
mut list := LinkedList<int>{}
|
||||
list.push(1)
|
||||
list.push(2)
|
||||
list.push(3)
|
||||
assert list.pop() ? == 3
|
||||
list.push(4)
|
||||
assert list.pop() ? == 4
|
||||
assert list.pop() ? == 2
|
||||
list = LinkedList<int>{}
|
||||
list.pop() or { return }
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_shift() ? {
|
||||
mut list := LinkedList<int>{}
|
||||
list.push(1)
|
||||
list.push(2)
|
||||
list.push(3)
|
||||
assert list.shift() ? == 1
|
||||
list.push(4)
|
||||
assert list.shift() ? == 2
|
||||
assert list.shift() ? == 3
|
||||
list = LinkedList<int>{}
|
||||
list.shift() or { return }
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_insert() ? {
|
||||
mut list := LinkedList<int>{}
|
||||
list.push(1)
|
||||
list.push(2)
|
||||
list.push(3)
|
||||
list.insert(1, 111) or { return }
|
||||
assert true
|
||||
}
|
||||
|
||||
fn test_prepend() ? {
|
||||
mut list := LinkedList<int>{}
|
||||
list.push(1)
|
||||
list.push(2)
|
||||
list.push(3)
|
||||
list.prepend(111)
|
||||
assert list.first() ? == 111
|
||||
}
|
||||
|
||||
fn test_str() ? {
|
||||
mut list := LinkedList<int>{}
|
||||
list.push(1)
|
||||
list.push(2)
|
||||
list.push(3)
|
||||
assert list.str() == '[1, 2, 3]'
|
||||
}
|
36
vlib/datatypes/queue.v
Normal file
36
vlib/datatypes/queue.v
Normal file
@ -0,0 +1,36 @@
|
||||
module datatypes
|
||||
|
||||
pub struct Queue<T> {
|
||||
mut:
|
||||
elements LinkedList<T>
|
||||
}
|
||||
|
||||
// is_empty checks if the queue is empty
|
||||
pub fn (queue Queue<T>) is_empty() bool {
|
||||
return queue.elements.is_empty()
|
||||
}
|
||||
|
||||
// len returns the length of the queue
|
||||
pub fn (queue Queue<T>) len() int {
|
||||
return queue.elements.len()
|
||||
}
|
||||
|
||||
// peek returns the head of the queue
|
||||
pub fn (queue Queue<T>) peek() ?T {
|
||||
return if !queue.is_empty() { queue.elements.first() ? } else { error('Queue is empty') }
|
||||
}
|
||||
|
||||
// push adds an element to the tail of the queue
|
||||
pub fn (mut queue Queue<T>) push(item T) {
|
||||
queue.elements.push(item)
|
||||
}
|
||||
|
||||
// pop removes the element at the head of the queue and returns it
|
||||
pub fn (mut queue Queue<T>) pop() ?T {
|
||||
return if !queue.is_empty() { queue.elements.shift() ? } else { error('Queue is empty') }
|
||||
}
|
||||
|
||||
// str returns a string representation of the queue
|
||||
pub fn (queue Queue<T>) str() string {
|
||||
return queue.elements.str()
|
||||
}
|
49
vlib/datatypes/queue_test.v
Normal file
49
vlib/datatypes/queue_test.v
Normal file
@ -0,0 +1,49 @@
|
||||
module datatypes
|
||||
|
||||
fn test_is_empty() {
|
||||
mut queue := Queue<int>{}
|
||||
assert queue.is_empty() == true
|
||||
queue.push(1)
|
||||
assert queue.is_empty() == false
|
||||
}
|
||||
|
||||
fn test_len() ? {
|
||||
mut queue := Queue<int>{}
|
||||
assert queue.len() == 0
|
||||
queue.push(1)
|
||||
assert queue.len() == 1
|
||||
queue.pop() ?
|
||||
assert queue.len() == 0
|
||||
}
|
||||
|
||||
fn test_peek() ? {
|
||||
mut queue := Queue<int>{}
|
||||
queue.push(1)
|
||||
assert queue.peek() ? == 1
|
||||
queue.push(2)
|
||||
assert queue.peek() ? == 1
|
||||
queue = Queue<int>{}
|
||||
queue.peek() or { return }
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_push() ? {
|
||||
mut queue := Queue<int>{}
|
||||
queue.push(1)
|
||||
queue.push(2)
|
||||
assert queue.peek() ? == 1
|
||||
}
|
||||
|
||||
fn test_pop() ? {
|
||||
mut queue := Queue<int>{}
|
||||
queue.push(1)
|
||||
queue.push(2)
|
||||
queue.push(3)
|
||||
assert queue.pop() ? == 1
|
||||
queue.push(4)
|
||||
assert queue.pop() ? == 2
|
||||
assert queue.pop() ? == 3
|
||||
queue = Queue<int>{}
|
||||
queue.pop() or { return }
|
||||
assert false
|
||||
}
|
36
vlib/datatypes/stack.v
Normal file
36
vlib/datatypes/stack.v
Normal file
@ -0,0 +1,36 @@
|
||||
module datatypes
|
||||
|
||||
pub struct Stack<T> {
|
||||
mut:
|
||||
elements []T
|
||||
}
|
||||
|
||||
// is_empty checks if the stack is empty
|
||||
pub fn (stack Stack<T>) is_empty() bool {
|
||||
return stack.elements.len == 0
|
||||
}
|
||||
|
||||
// len returns the length of the stack
|
||||
pub fn (stack Stack<T>) len() int {
|
||||
return stack.elements.len
|
||||
}
|
||||
|
||||
// peek returns the top of the stack
|
||||
pub fn (stack Stack<T>) peek() ?T {
|
||||
return if !stack.is_empty() { stack.elements.last() } else { error('Stack is empty') }
|
||||
}
|
||||
|
||||
// push adds an element to the top of the stack
|
||||
pub fn (mut stack Stack<T>) push(item T) {
|
||||
stack.elements << item
|
||||
}
|
||||
|
||||
// pop removes the element at the top of the stack and returns it
|
||||
pub fn (mut stack Stack<T>) pop() ?T {
|
||||
return if !stack.is_empty() { stack.elements.pop() } else { error('Stack is empty') }
|
||||
}
|
||||
|
||||
// str returns a string representation of the stack
|
||||
pub fn (stack Stack<T>) str() string {
|
||||
return stack.elements.str()
|
||||
}
|
52
vlib/datatypes/stack_test.v
Normal file
52
vlib/datatypes/stack_test.v
Normal file
@ -0,0 +1,52 @@
|
||||
import datatypes as dt
|
||||
|
||||
fn test_is_empty() {
|
||||
mut stack := dt.Stack<int>{}
|
||||
assert stack.is_empty() == true
|
||||
stack.push(1)
|
||||
assert stack.is_empty() == false
|
||||
}
|
||||
|
||||
fn test_len() ? {
|
||||
mut stack := dt.Stack<int>{}
|
||||
assert stack.len() == 0
|
||||
stack.push(1)
|
||||
assert stack.len() == 1
|
||||
stack.pop() ?
|
||||
assert stack.len() == 0
|
||||
}
|
||||
|
||||
fn test_peek() ? {
|
||||
mut stack := dt.Stack<int>{}
|
||||
stack.push(1)
|
||||
assert stack.peek() ? == 1
|
||||
stack.push(2)
|
||||
assert stack.peek() ? == 2
|
||||
stack = dt.Stack<int>{}
|
||||
stack.peek() or { return }
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_push() ? {
|
||||
mut stack := dt.Stack<int>{}
|
||||
stack.push(1)
|
||||
assert stack.peek() ? == 1
|
||||
stack.push(2)
|
||||
assert stack.peek() ? == 2
|
||||
stack.push(3)
|
||||
assert stack.peek() ? == 3
|
||||
}
|
||||
|
||||
fn test_pop() ? {
|
||||
mut stack := dt.Stack<int>{}
|
||||
stack.push(1)
|
||||
stack.push(2)
|
||||
stack.push(3)
|
||||
assert stack.pop() ? == 3
|
||||
stack.push(4)
|
||||
assert stack.pop() ? == 4
|
||||
assert stack.pop() ? == 2
|
||||
stack = dt.Stack<int>{}
|
||||
stack.pop() or { return }
|
||||
assert false
|
||||
}
|
Reference in New Issue
Block a user