lane

package module
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2022 License: MIT Imports: 2 Imported by: 22

README

Lane

MIT License Build Status Go Documentation Go Report Card Go Version

The Lane package provides implementations of generic Queue, PriorityQueue, Stack, and Deque data structures. It was designed with simplicity, performance, and concurrent usage in mind.

Installation

Using this package requires a working Go environment. See the install instructions for Go.

Go Modules are required when using this package. See the go blog guide on using Go Modules.

Using v2 releases
go get github.com/oleiade/lane/v2
...
import (
 "github.com/oleiade/lane/v2" // imports as package "cli"
)
...
Using v1 releases
go get github.com/oleiade/lane
...
import (
 "github.com/oleiade/lane"
)
...

Usage/Examples

Priority Queue

PriorityQueue is a heap priority queue data structure implementation. It can be either maximum (descending) or minimum (ascending) oriented (ordered). Every operation on a PriorityQueue are synchronized and goroutine-safe. It performs Push and Pop operations in O(log N) time.

Example
// Let's create a new max ordered priority queue
priorityQueue := NewMaxPriorityQueue[string]()

// And push some prioritized content into it
priorityQueue.Push("easy as", 3)
priorityQueue.Push("123", 2)
priorityQueue.Push("do re mi", 4)
priorityQueue.Push("abc", 1)

// Now let's take a look at the min element in
// the priority queue
headValue, headPriority, ok := priorityQueue.Head()
if okay {
    fmt.Println(headValue) // "abc"
    fmt.Println(headPriority) // 1
}

// okay, the song order seems to be preserved; let's
// roll
var jacksonFive []string = make([]string, priorityQueue.Size())

for i := 0; i < len(jacksonFive); i++ {
    value, _, _ := priorityQueue.Pop()
    jacksonFive[i] = value
}

fmt.Println(strings.Join(jacksonFive, " "))
Deque

Deque is a head-tail linked list data structure implementation. It is built upon a doubly-linked list container, and every operation on a Deque are performed in O(1) time complexity. Every operation on a Deque is synchronized and goroutine-safe.

Deques can optionally be instantiated with a limited capacity using the dedicated NewBoundDeque constructor, whereby the return value of the Append and Prepend return false if the Deque was full and the item was not added.

Deque Example
// Let's create a new deque data structure
deque := NewDeque[string]()

// And push some content into it using the Append
// and Prepend methods
deque.Append("easy as")
deque.Prepend("123")
deque.Append("do re mi")
deque.Prepend("abc")

// Now let's take a look at what are the first and
// last element stored in the Deque
firstValue, ok := deque.First()
if okay {
    fmt.Println(firstValue) // "abc"
}

lastValue, ok := deque.Last()
if ok {
    fmt.Println(lastValue) // 1
}

//okay, now let's play with the Pop and Shift
// methods to bring the song words together
var jacksonFive []string = make([]string, deque.Size())

for i := 0; i < len(jacksonFive); i++ {
    value, ok := deque.Shift()
    if okay {
        jacksonFive[i] = value
    }
}

// abc 123 easy as do re mi
fmt.Println(strings.Join(jacksonFive, " "))
Queue

Queue is a FIFO (First In First Out) data structure implementation. It is built upon a Deque container and focuses its API on core functionalities: Enqueue, Dequeue, Head. Every operation's time complexity is O(1). Every operation on a Queue is synchronized and goroutine-safe.

Queue Example
import (
"fmt"
"github.com/oleiade/lane/v2"
"sync"
)

func worker(item interface{}, wg *sync.WaitGroup) {
    fmt.Println(item)
    wg.Done()
}


func main() {
    // Create a new queue and pretend we're handling starbucks
    // clients
    queue := NewQueue[string]()

    // Let's add the incoming clients to the queue
    queue.Enqueue("grumpyClient")
    queue.Enqueue("happyClient")
    queue.Enqueue("ecstaticClient")

    fmt.Println(queue.Head()) // grumpyClient

    // Let's handle the clients asynchronously
    for client, okay:= queue.Dequeue(); ok; {
        go fmt.Println(client)
    }

    // Wait until everything is printed
    wg.Wait()
}
Stack

Stack is a LIFO ( Last in first out ) data structure implementation. It is built upon a Deque container and focuses its API on core functionalities: Push, Pop, Head. Every operation time complexity is O(1). Every operation on a Stack is synchronized and goroutine-safe.

Stack Example
// Create a new stack and put some plates over it
stack := NewStack[string]()

// Let's put some plates on the stack
stack.Push("redPlate")
stack.Push("bluePlate")
stack.Push("greenPlate")

fmt.Println(stack.Head()) // greenPlate

// What's on top of the stack?
value, okay:= stack.Pop()
if okay {
    fmt.Println(value) // greenPlate
}

stack.Push("yellowPlate")

value, ok = stack.Pop()
if okay {
    fmt.Println(value) // yellowPlate
}

// What's on top of the stack?
value, ok = stack.Pop()
if okay {
    fmt.Println(value) // bluePlate
}

// What's on top of the stack?
value, ok = stack.Pop()
if okay {
    fmt.Println(value) // redPlate
}

Performance

go test -bench=.
goos: darwin
goarch: arm64
pkg: github.com/oleiade/lane/v2
BenchmarkDequeAppend-8          22954384        54.44 ns/op      32 B/op       1 allocs/op
BenchmarkDequePrepend-8         25097098        44.59 ns/op      32 B/op       1 allocs/op
BenchmarkDequePop-8             63403720        18.99 ns/op       0 B/op       0 allocs/op
BenchmarkDequeShift-8           63390186        20.88 ns/op       0 B/op       0 allocs/op
BenchmarkDequeFirst-8           86662152        13.76 ns/op       0 B/op       0 allocs/op
BenchmarkDequeLast-8            85955014        13.76 ns/op       0 B/op       0 allocs/op
BenchmarkDequeSize-8            86614975        13.77 ns/op       0 B/op       0 allocs/op
BenchmarkDequeEmpty-8           86893297        14.22 ns/op       0 B/op       0 allocs/op
BenchmarkBoundDequeFull-8       590152324         2.039 ns/op       0 B/op       0 allocs/op
BenchmarkBoundDequeAppend-8     64457216        18.50 ns/op       0 B/op       0 allocs/op
BenchmarkBoundDeque-8           64631377        18.48 ns/op       0 B/op       0 allocs/op
BenchmarkPriorityQueuePush-8    19994029        65.67 ns/op      72 B/op       1 allocs/op
BenchmarkPriorityQueuePop-8     62751249        18.52 ns/op       0 B/op       0 allocs/op
BenchmarkPriorityQueueHead-8    86090420        13.77 ns/op       0 B/op       0 allocs/op
BenchmarkPriorityQueueSize-8    86768415        13.77 ns/op       0 B/op       0 allocs/op
BenchmarkPriorityQueueEmpty-8   87036146        13.76 ns/op       0 B/op       0 allocs/op
BenchmarkNewQueue-8             19570003        60.36 ns/op
BenchmarkQueueEnqueue-8         25482283        46.63 ns/op      32 B/op       1 allocs/op
BenchmarkQueueDequeue-8         63715965        18.50 ns/op       0 B/op       0 allocs/op
BenchmarkQueueHead-8            85664312        13.79 ns/op       0 B/op       0 allocs/op
BenchmarkNewStack-8             19925473        59.57 ns/op
BenchmarkStackPop-8             64704993        18.80 ns/op       0 B/op       0 allocs/op
BenchmarkStackHead-8            86119761        13.76 ns/op       0 B/op       0 allocs/op

Documentation

For a more detailed overview of lane, please refer to Documentation

License

MIT

Documentation

Overview

Package lane provides queue, priority queue, stack and deque data structures implementations. Its was designed with simplicity, performance, and concurrent usage in mind.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Maximum

func Maximum[T constraints.Ordered](lhs, rhs T) bool

Maximum returns whether `rhs` is greater than `lhs`.

It can be used as a comparison heuristic during a PriorityQueue's instantiation.

func Minimum

func Minimum[T constraints.Ordered](lhs, rhs T) bool

Minimum returns whether `rhs` is less than `lhs`.

It can be used as a comparison heuristic during a PriorityQueue's instantiation.

Types

type BoundDeque

type BoundDeque[T any] struct {
	Deque[T]
	// contains filtered or unexported fields
}

BoundDeque is a head-tail linked list data structure implementation with a user-defined capacity: any operation leading to the size of the container to overflow its capacity will fail.

The BoundDeque's implementation is built upon a doubly linked list container, so that every operations' time complexity is O(1) (N.B: linked-list are not CPU-cache friendly). Every operation on a BoundDeque are goroutine-safe and ready for concurrent usage.

func NewBoundDeque

func NewBoundDeque[T any](capacity uint, values ...T) *BoundDeque[T]

NewBoundDeque produces a new BoundDeque instance with the provided capacity.

func (*BoundDeque[T]) Append

func (bd *BoundDeque[T]) Append(item T) bool

Append inserts item at the back of the BoundDeque in a O(1) time complexity. If the BoundDeque's capacity disallows the insertion, Append returns false.

func (*BoundDeque[T]) Capacity

func (bd *BoundDeque[T]) Capacity() uint

Capacity returns the BoundDeque's capacity.

func (*BoundDeque[T]) Full

func (bd *BoundDeque[T]) Full() bool

Full checks if the BoundDeque is full.

func (*BoundDeque[T]) Prepend

func (bd *BoundDeque[T]) Prepend(item T) bool

Prepend inserts item at the BoundDeque's front in a O(1) time complexity. If the BoundDeque's capacity disallows the insertion, Prepend returns false.

type Capaciter

type Capaciter interface {
	// Capacity returns the current capacity of the underlying type implementation.
	Capacity() int

	// IsFull returns whether the implementing type instance is full.
	IsFull() bool
}

Capaciter is an interface type providing operations related to capacity management.

type Deque

type Deque[T any] struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

Deque is a head-tail linked list data structure implementation.

The Deque's implementation is built upon a doubly linked list container, so that every operations' time complexity is O(1) (N.B: linked-list are not CPU-cache friendly). Every operation on a Deque are goroutine-safe and ready for concurrent usage.

Example
// Let's create a new deque data structure
deque := NewDeque[string]()

// And push some content into it using the Append
// and Prepend methods
deque.Append("easy as")
deque.Prepend("123")
deque.Append("do re mi")
deque.Prepend("abc")

// Now let's take a look at what are the first and
// last element stored in the Deque
firstValue, ok := deque.First()
if ok {
	fmt.Println(firstValue) // "abc"
}

lastValue, ok := deque.Last()
if ok {
	fmt.Println(lastValue) // 1
}

// Okay now let's play with the Pop and Shift
// methods to bring the song words together
jacksonFive := make([]string, deque.Size())

for i := 0; i < len(jacksonFive); i++ {
	value, ok := deque.Shift()
	if ok {
		jacksonFive[i] = value
	}
}

// abc 123 easy as do re mi
fmt.Println(strings.Join(jacksonFive, " "))
Output:

func NewDeque

func NewDeque[T any](items ...T) *Deque[T]

NewDeque produces a new Deque instance.

func (*Deque[T]) Append

func (d *Deque[T]) Append(item T)

Append inserts item at the back of the Deque in a O(1) time complexity.

func (*Deque[T]) Empty

func (d *Deque[T]) Empty() bool

Empty checks if the deque is empty.

func (*Deque[T]) First

func (d *Deque[T]) First() (item T, ok bool)

First returns the first value stored in the Deque in O(1) time complexity.

func (*Deque[T]) Last

func (d *Deque[T]) Last() (item T, ok bool)

Last returns the last value stored in the Deque in O(1) time complexity.

func (*Deque[T]) Pop

func (d *Deque[T]) Pop() (item T, ok bool)

Pop removes and returns the back element of the Deque in O(1) time complexity.

func (*Deque[T]) Prepend

func (d *Deque[T]) Prepend(item T)

Prepend inserts item at the Deque's front in a O(1) time complexity.

func (*Deque[T]) Shift

func (d *Deque[T]) Shift() (item T, ok bool)

Shift removes and returns the front element of the Deque in O(1) time complexity.

func (*Deque[T]) Size

func (d *Deque[T]) Size() uint

Size returns the Deque's size.

type Dequer

type Dequer[T any] interface {
	Deque[T] | BoundDeque[T]
}

Dequer is the interface that wraps the basic Deque operations.

type Element

type Element[T any] struct {
	Value T
	// contains filtered or unexported fields
}

Element is a node of a linked list.

func (*Element[T]) Next

func (e *Element[T]) Next() *Element[T]

Next returns the next list element or nil.

func (*Element[T]) Prev

func (e *Element[T]) Prev() *Element[T]

Prev returns the previous list element or nil.

type List

type List[T any] struct {
	// contains filtered or unexported fields
}

List represents a doubly linked list.

func New

func New[T any]() *List[T]

New returns an initialized list.

func (*List[T]) Back

func (l *List[T]) Back() *Element[T]

Back returns the last element of list l or nil.

func (*List[T]) Front

func (l *List[T]) Front() *Element[T]

Front returns the first element of list l or nil.

func (*List[T]) Init

func (l *List[T]) Init() *List[T]

Init initializes or clears list l.

func (*List[T]) InsertAfter

func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T]

InsertAfter inserts a new element e with value v immediately after mark and returns e.

func (*List[T]) InsertBefore

func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T]

InsertBefore inserts a new element e with value v immediately before mark and returns e.

func (*List[T]) Len

func (l *List[T]) Len() uint

Len returns the number of elements of list l.

func (*List[T]) MoveAfter

func (l *List[T]) MoveAfter(e, mark *Element[T])

MoveAfter moves element e to its new position after mark.

func (*List[T]) MoveBefore

func (l *List[T]) MoveBefore(e, mark *Element[T])

MoveBefore moves element e to its new position before mark.

func (*List[T]) MoveToBack

func (l *List[T]) MoveToBack(e *Element[T])

MoveToBack moves element e to the back of list l.

func (*List[T]) MoveToFront

func (l *List[T]) MoveToFront(e *Element[T])

MoveToFront moves element e to the front of list l.

func (*List[T]) PushBack

func (l *List[T]) PushBack(v T) *Element[T]

PushBack inserts a new element e with value v at the back of list l and returns e.

func (*List[T]) PushBackList

func (l *List[T]) PushBackList(other *List[T])

PushBackList inserts a copy of an other list at the back of list l.

func (*List[T]) PushFront

func (l *List[T]) PushFront(v T) *Element[T]

PushFront inserts a new element e with value v at the front of list l and returns e.

func (*List[T]) PushFrontList

func (l *List[T]) PushFrontList(other *List[T])

PushFrontList inserts a copy of an other list at the front of list l.

func (*List[T]) Remove

func (l *List[T]) Remove(e *Element[T]) T

Remove removes e from l if e is an element of list l.

type PriorityQueue

type PriorityQueue[T any, P constraints.Ordered] struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

PriorityQueue is a heap priority queue data structure implementation.

It can be either be minimum (ascending) or maximum (descending) oriented/ordered. Its type parameters `T` and `P` respectively specify the value underlying type, and the priority underlying type.

Every operations are synchronized and goroutine-safe.

Example
// Let's create a new max ordered priority queue
priorityQueue := NewMaxPriorityQueue[string, int]()

// And push some prioritized content into it
priorityQueue.Push("easy as", 3)
priorityQueue.Push("123", 2)
priorityQueue.Push("do re mi", 4)
priorityQueue.Push("abc", 1)

// Now let's take a look at the min element in
// the priority queue
headValue, headPriority, ok := priorityQueue.Head()
if ok {
	fmt.Println(headValue)    // "abc"
	fmt.Println(headPriority) // 1
}

// Okay the song order seems to be preserved, let's
// roll
jacksonFive := make([]string, priorityQueue.Size())

for i := 0; i < len(jacksonFive); i++ {
	value, _, _ := priorityQueue.Pop()
	jacksonFive[i] = value
}

fmt.Println(strings.Join(jacksonFive, " "))
Output:

func NewMaxPriorityQueue

func NewMaxPriorityQueue[T any, P constraints.Ordered]() *PriorityQueue[T, P]

NewMaxPriorityQueue instantiates a new maximum oriented PriorityQueue.

func NewMinPriorityQueue

func NewMinPriorityQueue[T any, P constraints.Ordered]() *PriorityQueue[T, P]

NewMinPriorityQueue instantiates a new minimum oriented PriorityQueue.

func NewPriorityQueue

func NewPriorityQueue[T any, P constraints.Ordered](heuristic func(lhs, rhs P) bool) *PriorityQueue[T, P]

NewPriorityQueue instantiates a new PriorityQueue with the provided comparison heuristic. The package defines the `Max` and `Min` heuristic to define a maximum-oriented or minimum-oriented heuristic respectively.

func (*PriorityQueue[T, P]) Empty

func (pq *PriorityQueue[T, P]) Empty() bool

Empty returns whether the PriorityQueue is empty.

func (*PriorityQueue[T, P]) Head

func (pq *PriorityQueue[T, P]) Head() (value T, priority P, ok bool)

Head returns the highest or lowest priority item (depending on the comparison heuristic of your PriorityQueue) from the PriorityQueue in O(1) complexity.

func (*PriorityQueue[T, P]) Pop

func (pq *PriorityQueue[T, P]) Pop() (value T, priority P, ok bool)

Pop and returns the highest or lowest priority item (depending on the comparison heuristic of your PriorityQueue) from the PriorityQueue in at most O(log n) complexity.

func (*PriorityQueue[T, P]) Push

func (pq *PriorityQueue[T, P]) Push(value T, priority P)

Push inserts the value in the PriorityQueue with the provided priority in at most O(log n) time complexity.

func (*PriorityQueue[T, P]) Size

func (pq *PriorityQueue[T, P]) Size() uint

Size returns the number of elements present in the PriorityQueue.

type Queue

type Queue[T any] struct {
	// contains filtered or unexported fields
}

Queue is a FIFO (First in first out) data structure implementation. It is based on a Deque container and focuses its API on core functionalities: Enqueue, Dequeue, Head, Size, Empty.

Every operation's time complexity is O(1).

As it is implemented using a Deque container, every operations over an instiated Queue are synchronized and goroutine-safe.

Example
// Create a new queue and pretend we're handling starbucks
// clients
queue := NewQueue[string]()

// Let's add the incoming clients to the queue
queue.Enqueue("grumpyClient")
queue.Enqueue("happyClient")
queue.Enqueue("ecstaticClient")

fmt.Println(queue.Head()) // grumpyClient

// Let's handle the clients asynchronously
for client, ok := queue.Dequeue(); ok; {
	go fmt.Println(client)
}
Output:

func NewQueue

func NewQueue[T any](items ...T) *Queue[T]

NewQueue produces a new Queue instance.

func (*Queue[T]) Dequeue

func (q *Queue[T]) Dequeue() (item T, ok bool)

Dequeue removes and returns the Queue's front item in O(1) time complexity.

func (*Queue[T]) Enqueue

func (q *Queue[T]) Enqueue(item T)

Enqueue adds an item at the back of the Queue in O(1) time complexity.

func (*Queue[T]) Head

func (q *Queue[T]) Head() (item T, ok bool)

Head returns the Queue's front queue item in O(1) time complexity.

func (*Queue[T]) Size

func (q *Queue[T]) Size() uint

Size returns the size of the Queue.

type Stack

type Stack[T any] struct {
	// contains filtered or unexported fields
}

Stack is a LIFO (Last in first out) data structure implementation. It is based on a Deque container and focuses its API on core functionalities: Push, Pop, Head, Size, Empty.

Every operation's time complexity is O(1).

As it is implemented using a Deque container, every operations over an instiated Stack are synchronized and goroutine-safe.

Example
// Create a new stack and put some plates over it
stack := NewStack[string]()

// Let's put some plates on the stack
stack.Push("redPlate")
stack.Push("bluePlate")
stack.Push("greenPlate")

fmt.Println(stack.Head()) // greenPlate

// What's on top of the stack?
value, ok := stack.Pop()
if ok {
	fmt.Println(value) // greenPlate
}

stack.Push("yellowPlate")

value, ok = stack.Pop()
if ok {
	fmt.Println(value) // yellowPlate
}

// What's on top of the stack?
value, ok = stack.Pop()
if ok {
	fmt.Println(value) // bluePlate
}

// What's on top of the stack?
value, ok = stack.Pop()
if ok {
	fmt.Println(value) // redPlate
}
Output:

func NewStack

func NewStack[T any](items ...T) (stack *Stack[T])

NewStack produces a new Stack instance.

If any initialization variadic items are provided, they will be inserted as is: lower index being the head of stack.

func (*Stack[T]) Head

func (s *Stack[T]) Head() (item T, ok bool)

Head returns the item on the top of the Stack.

func (*Stack[T]) Pop

func (s *Stack[T]) Pop() (item T, ok bool)

Pop removes and returns the item on the top of the Stack.

func (*Stack[T]) Push

func (s *Stack[T]) Push(item T)

Push adds on an item on the top of the Stack.

func (*Stack[T]) Size

func (s *Stack[T]) Size() uint

Size returns the size of the Stack.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL