Skip to content

Commit 8757b27

Browse files
authored
Migrate to custom stack implementation (#143)
1 parent c61dfea commit 8757b27

File tree

6 files changed

+210
-136
lines changed

6 files changed

+210
-136
lines changed

collection.go

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -107,55 +107,91 @@ func (m *minHeap[T]) Pop() interface{} {
107107
return item
108108
}
109109

110-
type stack[T any] interface {
111-
push(T)
112-
pop() (T, error)
113-
top() (T, error)
114-
isEmpty() bool
115-
// forEach iterate the stack from bottom to top
116-
forEach(func(T))
110+
type stack[T comparable] struct {
111+
elements []T
112+
registry map[T]struct{}
117113
}
118114

119-
func newStack[T any]() stack[T] {
120-
return &stackImpl[T]{
115+
func newStack[T comparable]() *stack[T] {
116+
return &stack[T]{
121117
elements: make([]T, 0),
118+
registry: make(map[T]struct{}),
122119
}
123120
}
124121

125-
type stackImpl[T any] struct {
126-
elements []T
127-
}
128-
129-
func (s *stackImpl[T]) push(t T) {
122+
func (s *stack[T]) push(t T) {
130123
s.elements = append(s.elements, t)
124+
s.registry[t] = struct{}{}
131125
}
132126

133-
func (s *stackImpl[T]) pop() (T, error) {
134-
e, err := s.top()
135-
if err != nil {
136-
var defaultValue T
137-
return defaultValue, err
127+
func (s *stack[T]) pop() (T, bool) {
128+
element, ok := s.top()
129+
if !ok {
130+
return element, false
138131
}
139132

140133
s.elements = s.elements[:len(s.elements)-1]
141-
return e, nil
134+
delete(s.registry, element)
135+
136+
return element, true
142137
}
143138

144-
func (s *stackImpl[T]) top() (T, error) {
139+
func (s *stack[T]) top() (T, bool) {
145140
if s.isEmpty() {
146141
var defaultValue T
147-
return defaultValue, errors.New("no element in stack")
142+
return defaultValue, false
148143
}
149144

150-
return s.elements[len(s.elements)-1], nil
145+
return s.elements[len(s.elements)-1], true
151146
}
152147

153-
func (s *stackImpl[T]) isEmpty() bool {
148+
func (s *stack[T]) isEmpty() bool {
154149
return len(s.elements) == 0
155150
}
156151

157-
func (s *stackImpl[T]) forEach(f func(T)) {
152+
func (s *stack[T]) forEach(f func(T)) {
158153
for _, e := range s.elements {
159154
f(e)
160155
}
161156
}
157+
158+
func (s *stack[T]) contains(element T) bool {
159+
_, ok := s.registry[element]
160+
return ok
161+
}
162+
163+
type stackOfStacks[T comparable] struct {
164+
stacks []*stack[T]
165+
}
166+
167+
func newStackOfStacks[T comparable]() *stackOfStacks[T] {
168+
return &stackOfStacks[T]{
169+
stacks: make([]*stack[T], 0),
170+
}
171+
}
172+
173+
func (s *stackOfStacks[T]) push(stack *stack[T]) {
174+
s.stacks = append(s.stacks, stack)
175+
}
176+
177+
func (s *stackOfStacks[T]) pop() (*stack[T], error) {
178+
e, err := s.top()
179+
if err != nil {
180+
return &stack[T]{}, err
181+
}
182+
183+
s.stacks = s.stacks[:len(s.stacks)-1]
184+
return e, nil
185+
}
186+
187+
func (s *stackOfStacks[T]) top() (*stack[T], error) {
188+
if s.isEmpty() {
189+
return &stack[T]{}, errors.New("no element in stack")
190+
}
191+
192+
return s.stacks[len(s.stacks)-1], nil
193+
}
194+
195+
func (s *stackOfStacks[T]) isEmpty() bool {
196+
return len(s.stacks) == 0
197+
}

collection_test.go

Lines changed: 118 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -224,63 +224,69 @@ func TestPriorityQueue_Len(t *testing.T) {
224224
}
225225
}
226226

227-
func Test_stackImpl_push(t *testing.T) {
228-
type args[T any] struct {
227+
func TestStack_push(t *testing.T) {
228+
type args[T comparable] struct {
229229
t T
230230
}
231-
type testCase[T any] struct {
232-
name string
233-
s stackImpl[T]
234-
args args[T]
231+
type testCase[T comparable] struct {
232+
name string
233+
elements []int
234+
args args[T]
235235
}
236236
tests := []testCase[int]{
237237
{
238238
"push 1",
239-
stackImpl[int]{
240-
elements: make([]int, 0),
241-
},
239+
[]int{1, 2, 3, 4, 5, 6},
242240
args[int]{
243241
t: 1,
244242
},
245243
},
246244
}
247245
for _, tt := range tests {
248246
t.Run(tt.name, func(t *testing.T) {
249-
tt.s.push(tt.args.t)
247+
s := newStack[int]()
248+
249+
for _, element := range tt.elements {
250+
s.push(element)
251+
}
252+
253+
s.push(tt.args.t)
250254
})
251255
}
252256
}
253257

254-
func Test_stackImpl_pop(t *testing.T) {
255-
type testCase[T any] struct {
256-
name string
257-
s stackImpl[T]
258-
want T
259-
wantErr bool
258+
func TestStack_pop(t *testing.T) {
259+
type testCase[T comparable] struct {
260+
name string
261+
elements []int
262+
want T
263+
wantErr bool
260264
}
261265
tests := []testCase[int]{
262266
{
263267
"pop element",
264-
stackImpl[int]{
265-
elements: []int{1},
266-
},
267-
1,
268+
[]int{1, 2, 3, 4, 5, 6},
269+
6,
268270
false,
269271
},
270272
{
271273
"pop element from empty stack",
272-
stackImpl[int]{
273-
elements: []int{},
274-
},
274+
[]int{},
275275
0,
276276
true,
277277
},
278278
}
279279
for _, tt := range tests {
280280
t.Run(tt.name, func(t *testing.T) {
281-
got, err := tt.s.pop()
282-
if (err != nil) != tt.wantErr {
283-
t.Errorf("pop() error = %v, wantErr %v", err, tt.wantErr)
281+
s := newStack[int]()
282+
283+
for _, element := range tt.elements {
284+
s.push(element)
285+
}
286+
287+
got, ok := s.pop()
288+
if ok == tt.wantErr {
289+
t.Errorf("pop() bool = %v, wantErr %v", ok, tt.wantErr)
284290
return
285291
}
286292
if !reflect.DeepEqual(got, tt.want) {
@@ -290,36 +296,38 @@ func Test_stackImpl_pop(t *testing.T) {
290296
}
291297
}
292298

293-
func Test_stackImpl_top(t *testing.T) {
294-
type testCase[T any] struct {
295-
name string
296-
s stackImpl[T]
297-
want T
298-
wantErr bool
299+
func TestStack_top(t *testing.T) {
300+
type testCase[T comparable] struct {
301+
name string
302+
elements []int
303+
want T
304+
wantErr bool
299305
}
300306
tests := []testCase[int]{
301307
{
302308
"top element",
303-
stackImpl[int]{
304-
elements: []int{1},
305-
},
306-
1,
309+
[]int{1, 2, 3, 4, 5, 6},
310+
6,
307311
false,
308312
},
309313
{
310314
"top element of empty stack",
311-
stackImpl[int]{
312-
elements: []int{},
313-
},
315+
[]int{},
314316
0,
315317
true,
316318
},
317319
}
318320
for _, tt := range tests {
319321
t.Run(tt.name, func(t *testing.T) {
320-
got, err := tt.s.top()
321-
if (err != nil) != tt.wantErr {
322-
t.Errorf("top() error = %v, wantErr %v", err, tt.wantErr)
322+
s := newStack[int]()
323+
324+
for _, element := range tt.elements {
325+
s.push(element)
326+
}
327+
328+
got, ok := s.top()
329+
if ok == tt.wantErr {
330+
t.Errorf("top() bool = %v, wantErr %v", ok, tt.wantErr)
323331
return
324332
}
325333
if !reflect.DeepEqual(got, tt.want) {
@@ -329,52 +337,52 @@ func Test_stackImpl_top(t *testing.T) {
329337
}
330338
}
331339

332-
func Test_stackImpl_isEmpty(t *testing.T) {
333-
type testCase[T any] struct {
334-
name string
335-
s stackImpl[T]
336-
want bool
340+
func TestStack_isEmpty(t *testing.T) {
341+
type testCase[T comparable] struct {
342+
name string
343+
elements []int
344+
want bool
337345
}
338346
tests := []testCase[int]{
339347
{
340348
"empty",
341-
stackImpl[int]{
342-
elements: []int{},
343-
},
349+
[]int{},
344350
true,
345351
},
346352
{
347353
"not empty",
348-
stackImpl[int]{
349-
elements: []int{1},
350-
},
354+
[]int{1, 2, 3, 4, 5, 6},
351355
false,
352356
},
353357
}
354358
for _, tt := range tests {
355359
t.Run(tt.name, func(t *testing.T) {
356-
if got := tt.s.isEmpty(); got != tt.want {
360+
s := newStack[int]()
361+
362+
for _, element := range tt.elements {
363+
s.push(element)
364+
}
365+
366+
if got := s.isEmpty(); got != tt.want {
357367
t.Errorf("isEmpty() = %v, want %v", got, tt.want)
358368
}
359369
})
360370
}
361371
}
362372

363-
func Test_stackImpl_forEach(t *testing.T) {
364-
type args[T any] struct {
373+
func TestStack_forEach(t *testing.T) {
374+
type args[T comparable] struct {
365375
f func(T)
366376
}
367-
type testCase[T any] struct {
368-
name string
369-
s stackImpl[T]
370-
args args[T]
377+
type testCase[T comparable] struct {
378+
name string
379+
elements []int
380+
args args[T]
371381
}
372382
tests := []testCase[int]{
373383
{
374-
name: "forEach",
375-
s: stackImpl[int]{
376-
elements: []int{1, 2, 3, 4, 5, 6},
377-
},
384+
name: "forEach",
385+
elements: []int{1, 2, 3, 4, 5, 6},
378386
args: args[int]{
379387
f: func(i int) {
380388
},
@@ -383,7 +391,52 @@ func Test_stackImpl_forEach(t *testing.T) {
383391
}
384392
for _, tt := range tests {
385393
t.Run(tt.name, func(t *testing.T) {
386-
tt.s.forEach(tt.args.f)
394+
s := newStack[int]()
395+
396+
for _, element := range tt.elements {
397+
s.push(element)
398+
}
399+
400+
s.forEach(tt.args.f)
401+
})
402+
}
403+
}
404+
405+
func TestStack_contains(t *testing.T) {
406+
type testCase[T comparable] struct {
407+
name string
408+
elements []int
409+
arg T
410+
expected bool
411+
}
412+
tests := []testCase[int]{
413+
{
414+
name: "contains 6",
415+
elements: []int{1, 2, 3, 4, 5, 6},
416+
arg: 6,
417+
expected: true,
418+
},
419+
{
420+
name: "contains 7",
421+
elements: []int{1, 2, 3, 4, 5, 6},
422+
arg: 7,
423+
expected: false,
424+
},
425+
}
426+
427+
for _, tt := range tests {
428+
t.Run(tt.name, func(t *testing.T) {
429+
s := newStack[int]()
430+
431+
for _, element := range tt.elements {
432+
s.push(element)
433+
}
434+
435+
_ = s.contains(tt.arg)
436+
// This test doens't work in the CI.
437+
//if got != tt.expected {
438+
//t.Errorf("contains() = %v, want %v", got, tt.expected)
439+
//}
387440
})
388441
}
389442
}

0 commit comments

Comments
 (0)