diff options
| author | ruki <[email protected]> | 2024-10-07 23:49:31 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-10-07 23:49:31 +0800 |
| commit | 669596b986f75a9e10796538710ba510dadb8d05 (patch) | |
| tree | 9b10aec20c5c0e3aa30c4ddb031f333032528b59 | |
| parent | 6c973f55a3130fe62342e4ab06e992eac6ee3a80 (diff) | |
| parent | 5995fe8beb75a2f8e4438cdccc7186430953f5ed (diff) | |
Merge pull request #5688 from xmake-io/hashset
improve hashset
| -rw-r--r-- | tests/modules/hashset/test.lua | 26 | ||||
| -rw-r--r-- | xmake/core/base/hashset.lua | 195 |
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 |
