[added] set module with full (-ish?) suite of set operations and efficient (non-linear) membership testing

closes #4
This commit is contained in:
Max Cahill 2020-05-02 19:59:02 +10:00
parent 28e06c4a10
commit e5be0df492
2 changed files with 146 additions and 19 deletions

View File

@ -1,22 +1,7 @@
--[[
batteries for lua
if required as the "entire library" (ie by this file), puts everything into
global namespace by default as it'll presumably be commonly used
if not, several of the modules work as normal lua modules and return a table
for local-friendly use
the others that modify some global table can be talked into behaving as normal
lua modules as well by setting appropriate globals prior to inclusion
you can avoid modifying any global namespace by setting
BATTERIES_NO_GLOBALS = true
before requiring, then everything can be accessed as eg
batteries.table.stable_sort
a collection of helpful code to get your project off the ground faster
]]
local path = ...
@ -32,7 +17,9 @@ local _tablex = require_relative("tablex")
local _stable_sort = require_relative("stable_sort")
local _functional = require_relative("functional")
local _sequence = require_relative("sequence")
local _set = require_relative("set")
local _stringx = require_relative("stringx")
@ -69,8 +56,9 @@ local _batteries = {
sort = _stable_sort,
--
functional = _functional,
--
--collections
sequence = _sequence,
set = _set,
--geom
vec2 = _vec2,
vec3 = _vec3,
@ -97,8 +85,9 @@ function _batteries:export(self)
--functional module also available separate from table
functional = _functional
--export sequence
--export collections
sequence = _sequence
set = _set
--overlay onto math
_tablex.overlay(math, _mathx)
@ -111,7 +100,7 @@ function _batteries:export(self)
vec3 = _vec3
intersect = _intersect
--misc :)
--"misc" :)
unique_mapping = _unique_mapping
state_machine = _state_machine
async = _async

138
set.lua Normal file
View File

@ -0,0 +1,138 @@
--[[
set type with appropriate operations
]]
local path = (...):gsub("set", "")
local class = require(path .. "class")
local table = require(path .. "tablex") --shadow global table module
local set = class()
--construct a new set
--elements is an optional ordered table of elements to be added to the set
function set:new(elements)
self = self:init({
_keyed = {},
_ordered = {},
})
if elements then
for _, v in ipairs(elements) do
self:add(v)
end
end
return self
end
--check if an element is present in the set
function set:has(v)
return self._keyed[v] or false
end
--add a value to the set, if it's not already present
function set:add(v)
if not self:has(v) then
self._keyed[v] = true
table.insert(self._ordered, v)
end
return self
end
--remove a value from the set, if it's present
function set:remove(v)
if self:has(v) then
self._keyed[v] = nil
table.remove_value(self._ordered, v)
end
return self
end
--iterate the values in the set, along with their index
--the index is useless but harmless, and adding a custom iterator seems
--like a really easy way to encourage people to use slower-than-optimal code
function set:ipairs()
return ipairs(self._ordered)
end
--get a copy of the values in the set, as a simple table
function set:values()
return table.copy(self._ordered)
end
--modifying operations
--add all the elements present in the other set
function set:add_set(other)
for i, v in other:ipairs() do
self:add(v)
end
return self
end
--remove all the elements present in the other set
function set:subtract_set(other)
for i, v in other:ipairs() do
self:remove(v)
end
return self
end
--new collection operations
--copy a set
function set:copy()
return set:new():add_set(self)
end
--create a new set containing the complement of the other set contained in this one
--the elements present in this set but not present in the other set will remain in the result
function set:complement(other)
return self:copy():subtract_set(other)
end
--alias
set.difference = set.complement
--create a new set containing the union of this set with another
--an element present in either set will be present in the result
function set:union(other)
return self:copy():add_set(other)
end
--create a new set containing the intersection of this set with another
--only the elements present in both sets will remain in the result
function set:intersection(other)
local r = set:new()
for i, v in self:ipairs() do
if other:has(v) then
r:add(v)
end
end
return r
end
--create a new set containing the symmetric difference of this set with another
--only the elements not present in both sets will remain in the result
--similiar to a logical XOR operation
--
--equal to self:union(other):subtract_set(self:intersection(other))
-- but with much less wasted effort
function set:symmetric_difference(other)
local r = set:new()
for i, v in self:ipairs() do
if not other:has(v) then
r:add(v)
end
end
for i, v in other:ipairs() do
if not self:has(v) then
r:add(v)
end
end
return r
end
--alias
set.xor = set.symmetric_difference
--
return set