diff --git a/fundamentals/bag/bag.go b/fundamentals/bag/bag.go new file mode 100644 index 0000000..84e5e73 --- /dev/null +++ b/fundamentals/bag/bag.go @@ -0,0 +1,51 @@ +package bag + +// "Generic" item. +// When type parameters will land this type became +// type Item[T any] T +type Item interface{} + +type Bag interface { + Add(Item) + Size() int + IsEmpty() bool + ForEach(func(Item)) +} + +// We use linked list as internal data structure +// to get O(1) speed for Add operation +type node struct { + item Item + next *node +} + +type bag struct { + size int + head *node +} + +func NewBag() Bag { + return &bag{} +} + +func (b *bag) Add(item Item) { + next := b.head + b.head = &node{item, next} + b.size++ +} + +func (b *bag) Size() int { + return b.size +} + +func (b *bag) IsEmpty() bool { + return b.size == 0 +} + +// As for now Go doesn't have iterators. +// But we can simulate them with ForEach method +func (b *bag) ForEach(f func(Item)) { + for current := b.head; current != nil; current = current.next { + f(current.item) + } +} diff --git a/fundamentals/bag/bag_test.go b/fundamentals/bag/bag_test.go new file mode 100644 index 0000000..38c2ea0 --- /dev/null +++ b/fundamentals/bag/bag_test.go @@ -0,0 +1,36 @@ +package bag + +import ( + "log" + "testing" +) + +func TestSimple(t *testing.T) { + bag := NewBag() + sum := 0 + + bag.Add(1) + bag.Add(2) + bag.Add(3) + + bag.ForEach(func(i Item) { sum += i.(int) }) + + if sum != 6 { + log.Fatal("wrong items in bag") + } +} + +func TestEmpty(t *testing.T) { + bag := NewBag() + sum := 0 + + if bag.Size() != 0 || !bag.IsEmpty() { + log.Fatal("bag should be empty") + } + + bag.ForEach(func(i Item) { sum += i.(int) }) + + if sum != 0 { + log.Fatal("wrong items in bag") + } +} diff --git a/fundamentals/queue/queue.go b/fundamentals/queue/queue.go new file mode 100644 index 0000000..519e482 --- /dev/null +++ b/fundamentals/queue/queue.go @@ -0,0 +1,59 @@ +package queue + +// "Generic" item. +// When type parameters will land this type became +// type Item[T any] T +type Item interface{} + +type Queue interface { + Enqueue(Item) + Dequeue() Item + Size() int + IsEmpty() bool +} + +// We use linked list as internal data structure +// to get O(1) speed for push and pop opeartions +type node struct { + item Item + next *node +} + +type queue struct { + size int + head *node + tail *node +} + +func NewQueue() Queue { + return &queue{} +} + +func (q *queue) Enqueue(item Item) { + oldTail := q.tail + q.tail = &node{item: item, next: nil} + if q.IsEmpty() { + q.head = q.tail + } else { + oldTail.next = q.tail + } + q.size++ +} + +func (q *queue) Dequeue() Item { + first := q.head + q.head = q.head.next + if q.IsEmpty() { + q.tail = nil + } + q.size-- + return first.item +} + +func (q *queue) Size() int { + return q.size +} + +func (q *queue) IsEmpty() bool { + return q.head == nil +} diff --git a/fundamentals/queue/queue_test.go b/fundamentals/queue/queue_test.go new file mode 100644 index 0000000..32e0c20 --- /dev/null +++ b/fundamentals/queue/queue_test.go @@ -0,0 +1,35 @@ +package queue + +import ( + "log" + "testing" +) + +func TestSimple(t *testing.T) { + queue := NewQueue() + + queue.Enqueue(10) + queue.Enqueue(20) + queue.Enqueue(30) + + first, second := queue.Dequeue().(int), queue.Dequeue().(int) + + if first != 10 && second != 20 { + log.Fatal("wrong order") + } +} + +func TestSize(t *testing.T) { + queue := NewQueue() + + if queue.Size() != 0 { + log.Fatal("empty queue should have size 0") + } + queue.Enqueue(10) + queue.Enqueue(20) + queue.Enqueue(30) + + if queue.Size() != 3 { + log.Fatal("wrong size") + } +}