summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2024-10-07 23:49:31 +0800
committerGitHub <[email protected]>2024-10-07 23:49:31 +0800
commit669596b986f75a9e10796538710ba510dadb8d05 (patch)
tree9b10aec20c5c0e3aa30c4ddb031f333032528b59
parent6c973f55a3130fe62342e4ab06e992eac6ee3a80 (diff)
parent5995fe8beb75a2f8e4438cdccc7186430953f5ed (diff)
Merge pull request #5688 from xmake-io/hashset
improve hashset
-rw-r--r--tests/modules/hashset/test.lua26
-rw-r--r--xmake/core/base/hashset.lua195
2 files changed, 168 insertions, 53 deletions
diff --git a/tests/modules/hashset/test.lua b/tests/modules/hashset/test.lua
new file mode 100644
index 000000000..c7222dcac
--- /dev/null
+++ b/tests/modules/hashset/test.lua
@@ -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
+
diff --git a/xmake/core/base/hashset.lua b/xmake/core/base/hashset.lua
index 2a0449ad4..5d0ea761e 100644
--- a/xmake/core/base/hashset.lua
+++ b/xmake/core/base/hashset.lua
@@ -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
@@ -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
@@ -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
@@ -98,17 +101,72 @@ 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
@@ -116,7 +174,7 @@ end
-- end
-- @endcode
--
-function hashset_impl:keys()
+function hashset:keys()
return function (t, key)
local k, _ = next(t._DATA, key)
if k == hashset._NIL then
@@ -127,7 +185,7 @@ 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
@@ -135,7 +193,7 @@ end
-- 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
@@ -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