201 lines
5.9 KiB
Lua

--- A double queue.
-- Taken from ***Programming in Lua*** [Queues and Double Queues](http://www.lua.org/pil/11.4.html)
-- and modified to not allow nil values, and returns nil if @{pop_first} or @{pop_last} is used when the queue is empty.
-- @module Queue
-- @usage local Queue = require('stdlib/queue/queue')
local Queue = {_module_name = 'Queue'}
setmetatable(Queue, {__index = require('stdlib/core')})
local Is = require('stdlib/utils/is')
local t_size = table.size
--- Constructs a new Queue object.
-- @return (<span class="types">@{Queue}</span>) a new, empty queue
function Queue.new()
local queue = {first = 1, last = 0, objects = {}}
setmetatable(queue, Queue._mt)
return queue
end
Queue:set_caller(Queue.new)
-- Allows queue[3] to return the item at queue.objects[3]
local function __index(self, k)
if Is.number(k) then
return self.objects[k]
else
return rawget(self, k) or Queue[k]
end
end
-- Allows queue() to pop_first and queue(data) to push_last
local function __call(self, ...)
if ... then
return self.push(self, ...)
else
return self.pop(self)
end
end
--- Load global.queue or queues during on_load, as metatables are not persisted.
-- <p>This is only needed if you are using the queue as an object and storing it in global.
-- @param ... (<span class="types">@{Queue}</span>,...)
-- @usage global.myqueue1 = Queue.new()
-- global.myqueue2 = Queue.new()
-- script.on_load(function() Queue.load(myqueue1, myqueue2))
function Queue.load(...)
if not ... then
return
end
for _, queue in pairs({...}) do
if queue.first then
setmetatable(queue, Queue._mt)
end
end
end
-- --- Gets the index which matches the stored data
-- TODO future direction
-- function Queue.get_index(queue, obj)
-- for i, v in pairs(queue.objects) do
-- if v == obj then
-- return i
-- end
-- end
-- end
-- TODO pop at index, everything after index insert at -1 dec last
-- function Queue.pop_at_index(queue, index)
-- return queue.objects[index]
-- end
-- TODO push at index, everything at and after index + 1 inc last
-- function Queue.push_at_index(queue, index)
-- return queue
-- end
--- Push a new element to the front of the queue.
-- @param queue (<span class="types">@{Queue}</span>) the queue to push an element to
-- @tparam Mixed value the element to push
function Queue.push_first(queue, value)
Is.Assert(value, 'must have a value to push')
local first = queue.first - 1
queue.first = first
queue.objects[first] = value
return queue
end
--- Push a new element to the back of the queue.
-- @param queue (<span class="types">@{Queue}</span>) the queue to push an element to
-- @tparam Mixed value the element to push
function Queue.push_last(queue, value)
Is.Assert(value, 'must have a value to push')
local last = queue.last + 1
queue.last = last
queue.objects[last] = value
return queue
end
--- Shortcut for @{Queue.push_last}
-- @function Queue.push
Queue.push = Queue.push_last
--- Retrieve the element at the front of the queue and remove it from the queue.
-- @param queue (<span class="types">@{Queue}</span>) the queue to retrieve the element from
-- @treturn Mixed value the element at the front of the queue
function Queue.pop_first(queue)
if Queue.is_empty(queue) then
return nil
end
local first = queue.first
local value = queue.objects[first]
queue.objects[first] = nil -- to allow garbage collection
queue.first = first + 1
return value
end
--- Shortcut for @{Queue.pop_first}
-- @function Queue.pop
Queue.pop = Queue.pop_first
--- Return the element at the front of the queue and remove it from the queue.
-- @param queue (<span class="types">@{Queue}</span>) the queue to retrieve the element from
-- @treturn Mixed the element at the front of the queue
function Queue.peek_first(queue)
return queue.objects[queue.first]
end
--- Shortcut for @{Queue.peek_first}
-- @function Queue.peek
Queue.peek = Queue.peek_first
--- Retrieve the element at the back of the queue and remove it from the queue.
-- @param queue (<span class="types">@{Queue}</span>) the queue to retrieve the element from
-- @treturn Mixed the element at the back of the queue
function Queue.pop_last(queue)
if queue.is_empty(queue) then
return nil
end
local last = queue.last
local value = queue.objects[last]
queue.objects[last] = nil -- to allow garbage collection
queue.last = last - 1
return value
end
--- Return the element at the back of the queue.
-- @param queue (<span class="types">@{Queue}</span>) the queue to retrieve the element from
-- @treturn Mixed the element at the back of the queue
function Queue.peek_last(queue)
return queue.objects[queue.last]
end
--- Returns true if the given queue is empty.
-- @param queue (<span class="types">@{Queue}</span>) the queue to check
-- @treturn boolean true if empty, false otherwise
function Queue.is_empty(queue)
return t_size(queue.objects) == 0
end
--- Returns the number of items in the queue.
-- @param queue (<span class="types">@{Queue}</span>) the queue to check
-- @treturn number the number of items in the queue
function Queue.count(queue)
return t_size(queue.objects)
end
function Queue.pairs(queue, pop)
local i = queue.first - 1
return function()
i = i + 1
local v = pop and queue.pop(queue) or queue.objects[i]
if v then
return i, v
end
end
end
Queue.iter = Queue.pairs
Queue.iter_first = Queue.pairs
function Queue.rpairs(queue, pop)
local i = queue.last + 1
return function()
i = i - 1
local v = pop and queue.pop_last(queue) or queue.objects[i]
if v then
return i, v
end
end
end
Queue.iter_last = Queue.rpairs
Queue._mt = {
__index = __index,
__pairs = Queue.pairs,
__ipairs = Queue.pairs,
__len = Queue.count,
__call = __call
}
return Queue