-
-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(stdlib): Mutable queue and stack
- Loading branch information
1 parent
0fe8aa6
commit 33cb48c
Showing
7 changed files
with
904 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import Queue from "mutablequeue" | ||
import List from "list" | ||
|
||
let empty = Queue.make() | ||
assert Queue.isEmpty(empty) | ||
assert Queue.size(empty) == 0 | ||
assert Queue.peek(empty) == None | ||
assert Queue.pop(empty) == None | ||
assert Queue.size(empty) == 0 | ||
|
||
let queue = Queue.make() | ||
Queue.push(1, queue) | ||
Queue.push(2, queue) | ||
Queue.push(3, queue) | ||
|
||
assert !Queue.isEmpty(queue) | ||
assert Queue.size(queue) == 3 | ||
assert Queue.peek(queue) == Some(1) | ||
|
||
assert Queue.pop(queue) == Some(1) | ||
assert Queue.peek(queue) == Some(2) | ||
assert Queue.size(queue) == 2 | ||
|
||
Queue.push(4, queue) | ||
assert Queue.size(queue) == 3 | ||
assert Queue.peek(queue) == Some(2) | ||
let copy = Queue.copy(queue) | ||
Queue.pop(copy) | ||
assert Queue.size(copy) == 2 | ||
assert Queue.size(queue) == 3 | ||
Queue.clear(queue) | ||
assert Queue.size(queue) == 0 | ||
assert Queue.peek(queue) == None | ||
|
||
// test that expansion works | ||
let queue = Queue.makeSized(3) | ||
Queue.push(0, queue) | ||
Queue.push(1, queue) | ||
Queue.push(2, queue) | ||
Queue.push(3, queue) | ||
assert Queue.pop(queue) == Some(0) | ||
assert Queue.pop(queue) == Some(1) | ||
assert Queue.pop(queue) == Some(2) | ||
assert Queue.pop(queue) == Some(3) | ||
assert Queue.pop(queue) == None | ||
|
||
// test that the "circular" behavior of the circular queue works as expected | ||
let queue = Queue.makeSized(4) | ||
let push = x => () => Queue.push(x, queue) | ||
let pop = () => ignore(Queue.pop(queue)) | ||
let actions = [ | ||
push(1), | ||
push(2), | ||
push(3), | ||
push(4), | ||
pop, | ||
pop, | ||
pop, | ||
push(5), | ||
push(6), | ||
pop, | ||
pop, | ||
push(7), | ||
push(8), | ||
push(9), | ||
] | ||
List.forEach(action => action(), actions) | ||
|
||
assert Queue.size(queue) == 4 | ||
assert Queue.peek(queue) == Some(6) | ||
|
||
Queue.push(10, queue) | ||
assert Queue.size(queue) == 5 | ||
assert Queue.pop(queue) == Some(6) | ||
assert Queue.pop(queue) == Some(7) | ||
assert Queue.pop(queue) == Some(8) | ||
assert Queue.pop(queue) == Some(9) | ||
assert Queue.pop(queue) == Some(10) | ||
assert Queue.pop(queue) == None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import Stack from "mutablestack" | ||
|
||
let empty = Stack.make() | ||
assert Stack.isEmpty(empty) | ||
assert Stack.size(empty) == 0 | ||
assert Stack.peek(empty) == None | ||
assert Stack.pop(empty) == None | ||
assert Stack.size(empty) == 0 | ||
|
||
let stack = Stack.make() | ||
Stack.push(1, stack) | ||
Stack.push(2, stack) | ||
Stack.push(3, stack) | ||
|
||
assert !Stack.isEmpty(stack) | ||
assert Stack.size(stack) == 3 | ||
assert Stack.peek(stack) == Some(3) | ||
|
||
assert Stack.pop(stack) == Some(3) | ||
assert Stack.peek(stack) == Some(2) | ||
assert Stack.size(stack) == 2 | ||
|
||
Stack.push(4, stack) | ||
assert Stack.size(stack) == 3 | ||
assert Stack.peek(stack) == Some(4) | ||
let copy = Stack.copy(stack) | ||
Stack.pop(copy) | ||
assert Stack.size(copy) == 2 | ||
assert Stack.size(stack) == 3 | ||
Stack.clear(stack) | ||
assert Stack.size(stack) == 0 | ||
assert Stack.peek(stack) == None | ||
|
||
let stack = Stack.makeSized(4) | ||
|
||
Stack.push(1, stack) | ||
Stack.push(2, stack) | ||
Stack.push(3, stack) | ||
Stack.push(4, stack) | ||
Stack.push(5, stack) | ||
assert Stack.size(stack) == 5 | ||
assert Stack.pop(stack) == Some(5) | ||
assert Stack.pop(stack) == Some(4) | ||
assert Stack.pop(stack) == Some(3) | ||
assert Stack.pop(stack) == Some(2) | ||
assert Stack.pop(stack) == Some(1) | ||
assert Stack.pop(stack) == None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/** | ||
* @module MutableQueue: A mutable queue implementation. A queue is a FIFO (first-in-first-out) data structure where new values are added to the end and retrieved or removed from the beginning. | ||
* @example import MutableQueue from "mutablequeue" | ||
* @since v0.5.5 | ||
*/ | ||
|
||
import Array from "array" | ||
|
||
// a circular array-based queue implementation | ||
|
||
/** | ||
* @section Types: Type declarations included in the MutableQueue module. | ||
*/ | ||
|
||
// tail points to where the next element should be inserted | ||
|
||
/** | ||
* A mutable FIFO (first-in-first-out) data structure. | ||
*/ | ||
record Queue<a> { | ||
mut size: Number, | ||
mut array: Array<Option<a>>, | ||
mut headI: Number, | ||
mut tailI: Number, | ||
} | ||
|
||
/** | ||
* Creates a new queue with an initial storage of the given size. As values are | ||
* added or removed, the internal storage may grow or shrink. Generally, you | ||
* won’t need to care about the storage size of your map and can use | ||
* `MutableQueue.make()` instead. | ||
* | ||
* @param size: The initial storage size of the queue | ||
* @returns An empty queue | ||
* | ||
* @since v0.5.5 | ||
*/ | ||
export let makeSized = size => { | ||
{ size: 0, array: Array.make(size, None), headI: 0, tailI: 0 } | ||
} | ||
|
||
/** | ||
* Creates a new queue. | ||
* | ||
* @returns An empty queue | ||
* | ||
* @since v0.5.5 | ||
*/ | ||
export let make = () => { | ||
makeSized(16) | ||
} | ||
|
||
/** | ||
* Checks if the given queue contains no items. | ||
* | ||
* @param queue: The queue to check | ||
* @returns `true` if the queue has no items or `false` otherwise | ||
* | ||
* @since v0.5.5 | ||
*/ | ||
export let isEmpty = queue => queue.size == 0 | ||
|
||
/** | ||
* Computes the size of the input queue. | ||
* | ||
* @param queue: The queue to inspect | ||
* @returns The count of the items in the queue | ||
* | ||
* @since v0.5.5 | ||
*/ | ||
export let size = queue => queue.size | ||
|
||
/** | ||
* Provides the value at the beginning of the queue, if it exists. | ||
* | ||
* @param queue: The queue to inspect | ||
* @returns `Some(value)` containing the value at the beginning of the queue or `None` otherwise. | ||
* | ||
* @since v0.5.5 | ||
*/ | ||
export let peek = queue => { | ||
if (queue.size == 0) None else queue.array[queue.headI] | ||
} | ||
|
||
/** | ||
* Adds a new item to the end of the queue. | ||
* | ||
* @param value: The item to be added | ||
* @param queue: The queue being updated | ||
* | ||
* @since v0.5.5 | ||
*/ | ||
export let push = (value, queue) => { | ||
let arrLen = Array.length(queue.array) | ||
// expand the array if needed | ||
if (queue.size == arrLen) { | ||
let newArray = Array.make(arrLen * 2, None) | ||
|
||
newArray[0] = queue.array[queue.headI] | ||
let mut insertI = 1 | ||
let mut currI = (queue.headI + 1) % arrLen | ||
while (currI != queue.tailI) { | ||
newArray[insertI] = queue.array[currI] | ||
insertI += 1 | ||
currI = (currI + 1) % arrLen | ||
} | ||
|
||
queue.headI = 0 | ||
queue.tailI = arrLen | ||
queue.array = newArray | ||
} | ||
queue.array[queue.tailI] = Some(value) | ||
queue.tailI = (queue.tailI + 1) % Array.length(queue.array) | ||
queue.size += 1 | ||
} | ||
|
||
/** | ||
* Removes the item at the beginning of the queue. | ||
* | ||
* @param queue: The queue being updated | ||
* @returns The element removed from the queue | ||
* | ||
* @since v0.5.5 | ||
*/ | ||
export let pop = queue => { | ||
if (queue.size == 0) { | ||
None | ||
} else { | ||
let elem = queue.array[queue.headI] | ||
queue.array[queue.headI] = None | ||
queue.headI = (queue.headI + 1) % Array.length(queue.array) | ||
queue.size -= 1 | ||
elem | ||
} | ||
} | ||
|
||
/** | ||
* Clears the queue by removing all of its elements | ||
* | ||
* @param queue: The queue to clear | ||
* | ||
* @since v0.5.5 | ||
*/ | ||
export let clear = queue => { | ||
queue.size = 0 | ||
Array.fill(None, queue.array) | ||
queue.headI = 0 | ||
queue.tailI = 0 | ||
} | ||
|
||
/** | ||
* Produces a shallow copy of the input queue. | ||
* | ||
* @param queue: The queue to copy | ||
* @returns A new queue containing the elements from the input | ||
* | ||
* @since v0.5.5 | ||
*/ | ||
export let copy = queue => { | ||
let { size, array, headI, tailI } = queue | ||
{ size, array: Array.copy(array), headI, tailI } | ||
} |
Oops, something went wrong.