Skip to content

Commit ec0d08d

Browse files
committed
Add Set module and specs
1 parent e80711e commit ec0d08d

3 files changed

Lines changed: 518 additions & 0 deletions

File tree

spec/Set_spec.lua

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---@diagnostic disable: undefined-global
2+
3+
local Set = require("azrar.vendor.Set")
4+
local deepcopy = require("pl.tablex").deepcopy
5+
6+
local upper = string.upper
7+
8+
describe("azrar.vendor.Set", function()
9+
([[ _____ ___de _b_d_ _bcd_ a_c__ __c__
10+
a_c_e a___e abc__ abcd_ abcde
11+
ab_de ABC__ ]]):gsub("%S+", function(p)
12+
local set = Set()
13+
p:gsub(".", function(c)
14+
set[c] = c ~= "_" and true or nil
15+
end)
16+
_G[p] = set
17+
end)
18+
19+
-- stylua: ignore
20+
local tests = {
21+
--------------fname-------------|--set--|--arg--|-expected-|-same_ref?---
22+
{ "add" , _b_d_ , "c" , _bcd_ , true },
23+
{ "clear" , abcde , nil , {} , true },
24+
{ "copy" , abcde , nil , abcde , },
25+
{ "difference_update" , a_c_e , _bcd_ , a___e , true },
26+
{ "difference" , a_c_e , _bcd_ , a___e , },
27+
{ "discard" , _bcd_ , "c" , _b_d_ , true },
28+
{ "intersection_update" , a_c_e , _bcd_ , __c__ , true },
29+
{ "intersection" , a_c_e , _bcd_ , __c__ , },
30+
{ "isdisjoint" , _b_d_ , ___de , false , },
31+
{ "isdisjoint" , _b_d_ , a___e , true , },
32+
{ "isempty" , _____ , nil , true , },
33+
{ "isempty" , abcde , nil , false , },
34+
{ "issubset" , abcd_ , abcde , true , },
35+
{ "issubset" , abcde , abcd_ , false , },
36+
{ "issuperset" , abcd_ , abcde , false , },
37+
{ "issuperset" , abcde , abcd_ , true , },
38+
{ "len" , _____ , nil , 0 , },
39+
{ "len" , a_c__ , nil , 2 , },
40+
{ "map" , abc__ , upper , ABC__ , },
41+
{ "pop" , _____ , nil , nil , },
42+
{ "remove" , _bcd_ , "c" , _b_d_ , true },
43+
{ "symmetric_difference_update" , a_c_e , _bcd_ , ab_de , true },
44+
{ "symmetric_difference" , a_c_e , _bcd_ , ab_de , },
45+
{ "union" , abc__ , ___de , abcde , },
46+
{ "update" , abc__ , ___de , abcde , true },
47+
{ "values" , _____ , nil , {} , },
48+
}
49+
50+
for _, t in ipairs(tests) do
51+
local fname, set, arg_, expected, same_ref = table.unpack(t)
52+
it(fname .. "() returns correct value", function()
53+
set = deepcopy(set)
54+
local res = set[fname](set, arg_)
55+
assert.are_same(expected, res)
56+
if same_ref then
57+
assert.are_equal(set, res, "Expected same set instance")
58+
else
59+
assert.not_equal(set, res, "Expected different set instances")
60+
end
61+
end)
62+
end
63+
64+
describe("__call", function()
65+
it("constructs a set from a list", function()
66+
local s = Set({ "a", "b", "c" })
67+
assert.is_true(getmetatable(s) == Set)
68+
assert.are_same({ a = true, b = true, c = true }, s)
69+
end)
70+
71+
it("handles duplicate values", function()
72+
local s = Set({ "a", "b", "a", "c" })
73+
assert.are_same({ a = true, b = true, c = true }, s)
74+
end)
75+
end)
76+
77+
it("pop() removes and returns an arbitrary element", function()
78+
local s = Set({ "a", "b", "c" })
79+
local v = s:pop()
80+
assert.is_true(v == "a" or v == "b" or v == "c")
81+
assert.is_nil(s[v])
82+
end)
83+
84+
it("values() returns all values as a list", function()
85+
local ls = { "a", "b", "c" }
86+
local res = Set(ls):values()
87+
assert.are_same(ls, res:sort())
88+
end)
89+
end)

src/mods/Set.lua

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
local ok, mod = pcall(require, "mods.List")
2+
local List = ok and mod or nil
3+
4+
---@type mods.Set
5+
local Set = {}
6+
Set.__index = Set
7+
8+
function Set:add(v)
9+
self[v] = true
10+
return self
11+
end
12+
13+
function Set:clear()
14+
for k in pairs(self) do
15+
self[k] = nil
16+
end
17+
return self
18+
end
19+
20+
function Set:copy()
21+
return Set(self:values())
22+
end
23+
24+
function Set:difference_update(set)
25+
for k in pairs(set) do
26+
self[k] = nil
27+
end
28+
return self
29+
end
30+
31+
function Set:difference(set)
32+
local res = Set()
33+
for k in pairs(self) do
34+
if not set[k] then
35+
res[k] = true
36+
end
37+
end
38+
return res
39+
end
40+
41+
function Set:discard(v)
42+
self[v] = nil
43+
return self
44+
end
45+
46+
function Set:intersection_update(set)
47+
for k in pairs(self) do
48+
if not set[k] then
49+
self[k] = nil
50+
end
51+
end
52+
return self
53+
end
54+
55+
function Set:intersection(set)
56+
local res = Set()
57+
for k, _ in pairs(self) do
58+
if set[k] then
59+
res[k] = true
60+
end
61+
end
62+
return res
63+
end
64+
65+
function Set:isdisjoint(set)
66+
for k in pairs(self) do
67+
if set[k] then
68+
return false
69+
end
70+
end
71+
return true
72+
end
73+
74+
function Set.isempty(self)
75+
return not next(self)
76+
end
77+
78+
function Set:issubset(set)
79+
for k in pairs(self) do
80+
if not set[k] then
81+
return false
82+
end
83+
end
84+
return true
85+
end
86+
87+
function Set:issuperset(set)
88+
for k in pairs(set) do
89+
if not self[k] then
90+
return false
91+
end
92+
end
93+
return true
94+
end
95+
96+
function Set:len()
97+
local len = 0
98+
for _ in pairs(self) do
99+
len = len + 1
100+
end
101+
return len
102+
end
103+
104+
function Set:map(fn)
105+
local set = Set()
106+
for k in pairs(self) do
107+
set[fn(k)] = true
108+
end
109+
return set
110+
end
111+
112+
function Set:pop()
113+
local k = next(self)
114+
if k ~= nil then
115+
self[k] = nil
116+
return k
117+
end
118+
end
119+
120+
Set.remove = Set.discard
121+
122+
function Set:symmetric_difference_update(set)
123+
for k in pairs(set) do
124+
self[k] = not self[k] and true or nil
125+
end
126+
return self
127+
end
128+
129+
function Set:symmetric_difference(set)
130+
local res = self:difference(set)
131+
for k in pairs(set) do
132+
if not self[k] then
133+
res[k] = true
134+
end
135+
end
136+
return res
137+
end
138+
139+
function Set:union(set)
140+
local res = self:copy()
141+
for k in pairs(set) do
142+
res[k] = true
143+
end
144+
return res
145+
end
146+
147+
function Set:update(other)
148+
for k, v in pairs(other) do
149+
self[k] = v
150+
end
151+
return self
152+
end
153+
154+
function Set:values()
155+
local ls = List and List() or {}
156+
local i = 1
157+
for k in pairs(self) do
158+
ls[i] = k
159+
i = i + 1
160+
end
161+
return ls
162+
end
163+
164+
return setmetatable(Set, {
165+
__call = function(_, t)
166+
local set = {}
167+
for _, v in ipairs(t or {}) do
168+
set[v] = true
169+
end
170+
return setmetatable(set, Set)
171+
end,
172+
})

0 commit comments

Comments
 (0)