Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve hashset #5688

Merged
merged 5 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions tests/modules/hashset/test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import("core.base.hashset")

function test_hashset(t)
local h = hashset.of(1, 2, 3, 5, 5, 7, 1, 9, 4, 6, 8, 0)
t:require(h:size() == 10)
t:require_not(h:empty())
for item in h:items() do
t:require(h:has(item))
t:require_not(h:has(item + 10))
end
local prev = -1
for item in h:orderitems() do
t:require(item > prev)
prev = item
end
local h2 = h:clone()
t:require(h == h2)
h2:insert(11)
t:require_not(h == h2)
h2:remove(11)
t:require(h == h2)
h2:clear()
t:require(h2:empty())
t:require(h == hashset.from(h:to_array()))
end

195 changes: 142 additions & 53 deletions xmake/core/base/hashset.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,27 @@
--
-- Copyright (C) 2015-present, TBOOX Open Source Group.
--
-- @author OpportunityLiu
-- @author OpportunityLiu, ruki
-- @file hashset.lua
--

-- define module
local hashset = hashset or {}
local hashset_impl = hashset.__index or {}

-- load modules
local table = require("base/table")
local todisplay = require("base/todisplay")
local object = require("base/object")
local table = require("base/table")
local todisplay = require("base/todisplay")

-- representaion for nil key
hashset._NIL = setmetatable({}, { __todisplay = function() return "${reset}${color.dump.keyword}nil${reset}" end, __tostring = function() return "symbol(nil)" end })
-- define module
local hashset = hashset or object { _init = {"_DATA", "_SIZE"} }

function hashset:__todisplay()
return string.format("hashset${reset}(%s) {%s}", todisplay(self._SIZE), table.concat(table.imap(table.keys(self._DATA), function (i, k)
if i > 10 then
return nil
elseif i == 10 and self._SIZE ~= 10 then
return "..."
else
return todisplay(k)
end
end), ", "))
end
-- representaion for nil key
hashset._NIL = setmetatable({}, {
__todisplay = function()
return "${reset}${color.dump.keyword}nil${reset}"
end,
__tostring = function()
return "symbol(nil)"
end
})

function hashset._to_key(key)
if key == nil then
Expand All @@ -48,35 +43,43 @@ function hashset._to_key(key)
return key
end

-- make a new hashset
function hashset.new()
return setmetatable({ _DATA = {}, _SIZE = 0 }, hashset)
end

-- construct from list of items
function hashset.of(...)
local result = hashset.new()
local data = table.pack(...)
for i = 1, data.n do
result:insert(data[i])
-- h1 == h1?
function hashset:__eq(h)
if self._DATA == h._DATA then
return true
end
return result
if self:size() ~= h:size() then
return false
end
for item in h:items() do
if not self:has(item) then
return false
end
end
return true
end

-- construct from an array
function hashset.from(array)
assert(array)
return hashset.of(table.unpack(array))
-- to display
function hashset:__todisplay()
return string.format("hashset${reset}(%s) {%s}", todisplay(self._SIZE), table.concat(table.imap(table.keys(self._DATA), function (i, k)
if i > 10 then
return nil
elseif i == 10 and self._SIZE ~= 10 then
return "..."
else
return todisplay(k)
end
end), ", "))
end

-- check value is in hashset
function hashset_impl:has(value)
function hashset:has(value)
value = hashset._to_key(value)
return self._DATA[value] or false
end

-- insert value to hashset, returns false if value has already in the hashset
function hashset_impl:insert(value)
function hashset:insert(value)
value = hashset._to_key(value)
local result = not (self._DATA[value] or false)
if result then
Expand All @@ -87,7 +90,7 @@ function hashset_impl:insert(value)
end

-- remove value from hashset, returns false if value is not in the hashset
function hashset_impl:remove(value)
function hashset:remove(value)
value = hashset._to_key(value)
local result = self._DATA[value] or false
if result then
Expand All @@ -98,25 +101,80 @@ function hashset_impl:remove(value)
end

-- convert hashset to an array, nil in the set will be ignored
function hashset_impl:to_array()
function hashset:to_array()
local result = {}
for k, _ in pairs(self._DATA) do
if k ~= hashset._NIL then
table.insert(result, k)
for item in self:items() do
if item ~= nil then
table.insert(result, item)
end
end
return result
end

-- iterate keys of hashtable
-- iterate items
--
-- @code
-- for item in instance:items() do
-- ...
-- end
-- @endcode
--
function hashset:items()
return function (t, item)
local k, _ = next(t._DATA, item)
if k == hashset._NIL then
return nil
else
return k
end
end, self, nil
end

-- iterate order items
--
-- @code
-- for item in instance:orderitems() do
-- ...
-- end
-- @endcode
--
function hashset:orderitems()
local orderkeys = table.orderkeys(self._DATA, function (a, b)
if a == hashset._NIL then
a = math.inf
end
if b == hashset._NIL then
b = math.inf
end
if type(a) == "table" then
a = tostring(a)
end
if type(b) == "table" then
b = tostring(b)
end
return a < b
end)
local i = 1
return function (t, k)
k = orderkeys[i]
i = i + 1
if k == hashset._NIL then
return nil
else
return k
end
end, self, nil
end

-- iterate keys (deprecated, please use items())
--
-- @code
-- for _, key in instance:keys() do
-- ...
-- end
-- @endcode
--
function hashset_impl:keys()
function hashset:keys()
return function (t, key)
local k, _ = next(t._DATA, key)
if k == hashset._NIL then
Expand All @@ -127,15 +185,15 @@ function hashset_impl:keys()
end, self, nil
end

-- order keys iterator
-- iterate order keys (deprecated, please use orderitems())
--
-- @code
-- for _, key in instance:orderkeys() do
-- ...
-- end
-- @endcode
--
function hashset_impl:orderkeys()
function hashset:orderkeys()
local orderkeys = table.keys(self._DATA)
table.sort(orderkeys, function (a, b)
if a == hashset._NIL then
Expand Down Expand Up @@ -165,26 +223,57 @@ function hashset_impl:orderkeys()
end

-- get size of hashset
function hashset_impl:size()
function hashset:size()
return self._SIZE
end

-- is empty?
function hashset_impl:empty()
function hashset:empty()
return self:size() == 0
end

-- get data of hashset
function hashset_impl:data()
function hashset:data()
return self._DATA
end

-- clear hashset
function hashset_impl:clear()
function hashset:clear()
self._DATA = {}
self._SIZE = 0
end

-- return module
hashset.__index = hashset_impl
-- clone hashset
function hashset:clone()
local h = hashset.new()
h._SIZE = self._SIZE
h._DATA = table.clone(self._DATA)
return h
end

-- construct from list of items
function hashset.of(...)
local result = hashset.new()
local data = table.pack(...)
for i = 1, data.n do
result:insert(data[i])
end
return result
end

-- construct from an array
function hashset.from(array)
local result = hashset.new()
for i = 1, #array do
result:insert(array[i])
end
return result
end

-- new hashset
function hashset.new()
return hashset {{}, 0}
end

-- return module: hashset
return hashset
Loading