Merge branch 'master' into hw06_pipeline_execution
commit
a9c95260a7
|
@ -1,22 +1,79 @@
|
||||||
package hw04_lru_cache //nolint:golint,stylecheck
|
package hw04_lru_cache //nolint:golint,stylecheck
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
type Key string
|
type Key string
|
||||||
|
|
||||||
type Cache interface {
|
type Cache interface {
|
||||||
// Place your code here
|
Set(key Key, value interface{}) bool // Добавить значение в кэш по ключу
|
||||||
|
Get(key Key) (interface{}, bool) // Получить значение из кэша по ключу
|
||||||
|
Clear() // Очистить кэш
|
||||||
}
|
}
|
||||||
|
|
||||||
type lruCache struct {
|
type lruCache struct {
|
||||||
// Place your code here:
|
capacity int
|
||||||
// - capacity
|
queue *List
|
||||||
// - queue
|
items map[Key]*ListItem
|
||||||
// - items
|
mx sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type cacheItem struct {
|
type Item struct {
|
||||||
// Place your code here
|
Key Key
|
||||||
|
Value interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCache(capacity int) Cache {
|
func NewCache(capacity int) Cache {
|
||||||
return &lruCache{}
|
return &lruCache{
|
||||||
|
capacity: capacity,
|
||||||
|
queue: NewList(),
|
||||||
|
items: make(map[Key]*ListItem),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lruCache) Set(key Key, value interface{}) bool {
|
||||||
|
if _, exists := l.items[key]; exists {
|
||||||
|
l.mx.RLock()
|
||||||
|
l.items[key].Value = Item{Value: value, Key: key}
|
||||||
|
l.queue.MoveToFront(l.items[key])
|
||||||
|
l.mx.RUnlock()
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
if l.queue.Len() == l.capacity {
|
||||||
|
l.mx.RLock()
|
||||||
|
k, ok := l.queue.Back().Value.(Item)
|
||||||
|
if !ok {
|
||||||
|
log.Fatal("Ошибка приведения типов")
|
||||||
|
}
|
||||||
|
delete(l.items, k.Key)
|
||||||
|
l.queue.Remove(l.queue.Back())
|
||||||
|
l.mx.RUnlock()
|
||||||
|
}
|
||||||
|
l.mx.RLock()
|
||||||
|
l.items[key] = l.queue.PushFront(Item{Value: value, Key: key})
|
||||||
|
l.mx.RUnlock()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lruCache) Get(key Key) (interface{}, bool) {
|
||||||
|
l.mx.Lock()
|
||||||
|
defer l.mx.Unlock()
|
||||||
|
if l.items[key] == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
l.queue.MoveToFront(l.items[key])
|
||||||
|
s, ok := l.items[key].Value.(Item)
|
||||||
|
if !ok {
|
||||||
|
log.Fatal("Ошибка приведения типов")
|
||||||
|
}
|
||||||
|
return s.Value, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lruCache) Clear() {
|
||||||
|
l.mx.Lock()
|
||||||
|
l.items = nil
|
||||||
|
l.queue.len = 0
|
||||||
|
l.queue.Info = ListItem{}
|
||||||
|
l.mx.Unlock()
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,12 +50,35 @@ func TestCache(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("purge logic", func(t *testing.T) {
|
t.Run("purge logic", func(t *testing.T) {
|
||||||
// Write me
|
c := NewCache(3)
|
||||||
|
|
||||||
|
wasInCache := c.Set("aaa", 100)
|
||||||
|
require.False(t, wasInCache)
|
||||||
|
|
||||||
|
wasInCache = c.Set("bbb", 200)
|
||||||
|
require.False(t, wasInCache)
|
||||||
|
|
||||||
|
wasInCache = c.Set("ccc", 300)
|
||||||
|
require.False(t, wasInCache)
|
||||||
|
|
||||||
|
_, ok := c.Get("bbb")
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
_, ok = c.Get("aaa")
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
wasInCache = c.Set("ddd", 400)
|
||||||
|
require.False(t, wasInCache)
|
||||||
|
|
||||||
|
_, ok = c.Get("ddd")
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
_, ok = c.Get("ccc")
|
||||||
|
require.False(t, ok)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCacheMultithreading(t *testing.T) {
|
func TestCacheMultithreading(t *testing.T) {
|
||||||
t.Skip() // Remove if task with asterisk completed
|
|
||||||
|
|
||||||
c := NewCache(10)
|
c := NewCache(10)
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module github.com/fixme_my_friend/hw04_lru_cache
|
module github.com/tiburon-777/HW_OTUS/hw04_lru_cache
|
||||||
|
|
||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,101 @@
|
||||||
package hw04_lru_cache //nolint:golint,stylecheck
|
package hw04_lru_cache //nolint:golint,stylecheck
|
||||||
|
|
||||||
type List interface {
|
type ListInterface interface {
|
||||||
// Place your code here
|
Len() int // длина списка
|
||||||
|
Front() *ListItem // первый Item
|
||||||
|
Back() *ListItem // последний Item
|
||||||
|
PushFront(v interface{}) *ListItem // добавить значение в начало
|
||||||
|
PushBack(v interface{}) *ListItem // добавить значение в конец
|
||||||
|
Remove(i *ListItem) // удалить элемент
|
||||||
|
MoveToFront(i *ListItem) // переместить элемент в начало
|
||||||
}
|
}
|
||||||
|
|
||||||
type listItem struct {
|
type ListItem struct {
|
||||||
// Place your code here
|
Value interface{} // значение
|
||||||
|
Next *ListItem // следующий элемент
|
||||||
|
Prev *ListItem // предыдущий элемент
|
||||||
}
|
}
|
||||||
|
|
||||||
type list struct {
|
type List struct {
|
||||||
// Place your code here
|
Info ListItem
|
||||||
|
len int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewList() List {
|
func NewList() *List {
|
||||||
return &list{}
|
return &List{len: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *List) Len() int {
|
||||||
|
return l.len
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *List) Front() *ListItem {
|
||||||
|
return l.Info.Next
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *List) Back() *ListItem {
|
||||||
|
return l.Info.Prev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *List) PushFront(v interface{}) *ListItem {
|
||||||
|
e := &ListItem{Value: v}
|
||||||
|
if l.len != 0 {
|
||||||
|
e.Prev = l.Info.Next
|
||||||
|
l.Info.Next.Next = e
|
||||||
|
} else {
|
||||||
|
l.Info.Prev = e
|
||||||
|
}
|
||||||
|
l.Info.Next = e
|
||||||
|
l.len++
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *List) PushBack(v interface{}) *ListItem {
|
||||||
|
e := &ListItem{Value: v}
|
||||||
|
if l.len != 0 {
|
||||||
|
e.Next = l.Info.Prev
|
||||||
|
l.Info.Prev.Prev = e
|
||||||
|
} else {
|
||||||
|
l.Info.Next = e
|
||||||
|
}
|
||||||
|
l.Info.Prev = e
|
||||||
|
l.len++
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *List) Remove(i *ListItem) {
|
||||||
|
if i.Prev == nil {
|
||||||
|
i.Prev = &ListItem{}
|
||||||
|
l.Info.Prev = i.Next
|
||||||
|
}
|
||||||
|
if i.Next == nil {
|
||||||
|
i.Next = &ListItem{}
|
||||||
|
l.Info.Next = i.Prev
|
||||||
|
}
|
||||||
|
i.Prev.Next = i.Next
|
||||||
|
i.Next.Prev = i.Prev
|
||||||
|
if l.Len() > 1 {
|
||||||
|
l.Info.Next.Next = nil
|
||||||
|
l.Info.Prev.Prev = nil
|
||||||
|
} else {
|
||||||
|
l.Info = ListItem{}
|
||||||
|
}
|
||||||
|
l.len--
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *List) MoveToFront(i *ListItem) {
|
||||||
|
if l.Info.Next == i {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if i.Prev != nil {
|
||||||
|
i.Prev.Next = i.Next
|
||||||
|
i.Next.Prev = i.Prev
|
||||||
|
} else {
|
||||||
|
i.Next.Prev = i.Prev
|
||||||
|
l.Info.Prev = i.Next
|
||||||
|
}
|
||||||
|
i.Prev = l.Front()
|
||||||
|
l.Front().Next = i
|
||||||
|
i.Next = nil
|
||||||
|
l.Info.Next = i
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,50 @@ func TestList(t *testing.T) {
|
||||||
t.Run("complex", func(t *testing.T) {
|
t.Run("complex", func(t *testing.T) {
|
||||||
l := NewList()
|
l := NewList()
|
||||||
|
|
||||||
l.PushFront(10) // [10]
|
l.PushFront(10)
|
||||||
l.PushBack(20) // [10, 20]
|
require.Equal(t, []int{10}, transpond(l))
|
||||||
l.PushBack(30) // [10, 20, 30]
|
require.Equal(t, 1, l.Len())
|
||||||
require.Equal(t, l.Len(), 3)
|
|
||||||
|
l.Remove(l.Front())
|
||||||
|
require.Equal(t, []int{}, transpond(l))
|
||||||
|
require.Equal(t, 0, l.Len())
|
||||||
|
|
||||||
|
l.PushFront(10)
|
||||||
|
require.Equal(t, []int{10}, transpond(l))
|
||||||
|
require.Equal(t, 1, l.Len())
|
||||||
|
|
||||||
|
l.PushBack(20)
|
||||||
|
require.Equal(t, []int{20, 10}, transpond(l))
|
||||||
|
require.Equal(t, 2, l.Len())
|
||||||
|
|
||||||
|
l.PushBack(30)
|
||||||
|
require.Equal(t, []int{30, 20, 10}, transpond(l))
|
||||||
|
require.Equal(t, 3, l.Len())
|
||||||
|
|
||||||
middle := l.Back().Next // 20
|
middle := l.Back().Next // 20
|
||||||
l.Remove(middle) // [10, 30]
|
l.Remove(middle)
|
||||||
require.Equal(t, l.Len(), 2)
|
require.Equal(t, []int{30, 10}, transpond(l))
|
||||||
|
require.Equal(t, 2, l.Len())
|
||||||
|
|
||||||
|
l.PushFront(20)
|
||||||
|
require.Equal(t, []int{30, 10, 20}, transpond(l))
|
||||||
|
require.Equal(t, 3, l.Len())
|
||||||
|
|
||||||
|
l.Remove(l.Back())
|
||||||
|
require.Equal(t, []int{10, 20}, transpond(l))
|
||||||
|
require.Equal(t, 2, l.Len())
|
||||||
|
|
||||||
|
l.PushBack(30)
|
||||||
|
require.Equal(t, []int{30, 10, 20}, transpond(l))
|
||||||
|
require.Equal(t, 3, l.Len())
|
||||||
|
|
||||||
|
l.Remove(l.Front())
|
||||||
|
require.Equal(t, []int{30, 10}, transpond(l))
|
||||||
|
require.Equal(t, 2, l.Len())
|
||||||
|
|
||||||
|
l.PushFront(20)
|
||||||
|
require.Equal(t, []int{30, 10, 20}, transpond(l))
|
||||||
|
require.Equal(t, 3, l.Len())
|
||||||
|
|
||||||
for i, v := range [...]int{40, 50, 60, 70, 80} {
|
for i, v := range [...]int{40, 50, 60, 70, 80} {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
|
@ -33,19 +69,28 @@ func TestList(t *testing.T) {
|
||||||
} else {
|
} else {
|
||||||
l.PushBack(v)
|
l.PushBack(v)
|
||||||
}
|
}
|
||||||
} // [80, 60, 40, 10, 30, 50, 70]
|
}
|
||||||
|
require.Equal(t, []int{70, 50, 30, 10, 20, 40, 60, 80}, transpond(l))
|
||||||
|
require.Equal(t, 8, l.Len())
|
||||||
|
|
||||||
require.Equal(t, l.Len(), 7)
|
l.MoveToFront(l.Front())
|
||||||
require.Equal(t, 80, l.Front().Value)
|
require.Equal(t, []int{70, 50, 30, 10, 20, 40, 60, 80}, transpond(l))
|
||||||
require.Equal(t, 70, l.Back().Value)
|
require.Equal(t, 8, l.Len())
|
||||||
|
|
||||||
l.MoveToFront(l.Front()) // [80, 60, 40, 10, 30, 50, 70]
|
l.MoveToFront(l.Back().Next.Next) // 30
|
||||||
l.MoveToFront(l.Back()) // [70, 80, 60, 40, 10, 30, 50]
|
require.Equal(t, []int{70, 50, 10, 20, 40, 60, 80, 30}, transpond(l))
|
||||||
|
require.Equal(t, 8, l.Len())
|
||||||
|
|
||||||
|
l.MoveToFront(l.Back())
|
||||||
|
require.Equal(t, []int{50, 10, 20, 40, 60, 80, 30, 70}, transpond(l))
|
||||||
|
require.Equal(t, 8, l.Len())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func transpond(l *List) []int {
|
||||||
elems := make([]int, 0, l.Len())
|
elems := make([]int, 0, l.Len())
|
||||||
for i := l.Back(); i != nil; i = i.Next {
|
for i := l.Back(); i != nil; i = i.Next {
|
||||||
elems = append(elems, i.Value.(int))
|
elems = append(elems, i.Value.(int))
|
||||||
}
|
}
|
||||||
require.Equal(t, []int{50, 30, 10, 40, 60, 80, 70}, elems)
|
return elems
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue