1
0
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:
Delyan Angelov
2021-12-26 16:01:36 +02:00
parent 2210f89ea3
commit 8a10dbcf27
11 changed files with 19 additions and 19 deletions

29
vlib/datatypes/README.md Normal file
View 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)
- [ ] ...

View 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
}

View 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
View 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
}

View 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
}

View 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()
}

View 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
View 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()
}

View 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
View 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()
}

View 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
}