adt: implement a doubly linked list (#12950)
							parent
							
								
									c0dcd1a9a5
								
							
						
					
					
						commit
						d69d2c600b
					
				|  | @ -0,0 +1,284 @@ | |||
| module adt | ||||
| 
 | ||||
| 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 | ||||
| } | ||||
|  | @ -0,0 +1,160 @@ | |||
| module adt | ||||
| 
 | ||||
| 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]' | ||||
| } | ||||
		Loading…
	
		Reference in New Issue