From 89a2446ceff584f9f079a7f6c74e20a38a3c7109 Mon Sep 17 00:00:00 2001 From: Andrey Ivanov Date: Mon, 22 Jun 2020 17:06:12 +0300 Subject: [PATCH 1/4] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=BC=D0=B5=D0=B6=D1=83?= =?UTF-8?q?=D1=82=D0=BE=D1=87=D0=BD=D1=8B=D0=B9=20=D0=BA=D0=BE=D0=BC=D0=BC?= =?UTF-8?q?=D0=B8=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hw04_lru_cache/cache.go | 50 +++++++++++++++--- hw04_lru_cache/cache_test.go | 26 ++++++++- hw04_lru_cache/go.mod | 2 +- hw04_lru_cache/list.go | 100 ++++++++++++++++++++++++++++++++--- hw04_lru_cache/list_test.go | 83 ++++++++++++++++++++++------- 5 files changed, 224 insertions(+), 37 deletions(-) diff --git a/hw04_lru_cache/cache.go b/hw04_lru_cache/cache.go index 738399e..dd93cfd 100644 --- a/hw04_lru_cache/cache.go +++ b/hw04_lru_cache/cache.go @@ -3,20 +3,54 @@ package hw04_lru_cache //nolint:golint,stylecheck type Key string type Cache interface { - // Place your code here + Set(key Key, value interface{}) bool // Добавить значение в кэш по ключу + Get(key Key) (interface{}, bool) // Получить значение из кэша по ключу + Clear() // Очистить кэш } type lruCache struct { - // Place your code here: - // - capacity - // - queue - // - items + capacity int + queue *List + items map[Key]*ListItem } -type cacheItem struct { - // Place your code here +type Item struct { + Key Key + Value interface{} } func NewCache(capacity int) Cache { - return &lruCache{} + return &lruCache{ + capacity: capacity, + queue: NewList(), + items: map[Key]*ListItem{}, + } +} + +func (l *lruCache) Set(key Key, value interface{}) bool { + if _, exists := l.Get(key); exists { + l.items[key].Value = value + return exists + } + if l.queue.Len() >= l.capacity { + // TODO: нужно как-то удалить элемент из мапа + //delete(l.items,l.queue.Back().(*Item).Key) + l.queue.Remove(l.queue.Back()) + } + l.items[key] = l.queue.PushFront(value) + return false +} + +func (l *lruCache) Get(key Key) (interface{}, bool) { + if l.items[key] == nil { + return nil, false + } + l.queue.MoveToFront(l.items[key]) + return l.items[key].Value, true +} + +func (l *lruCache) Clear() { + l.items = nil + l.queue.len = 0 + l.queue.Info = ListItem{} } diff --git a/hw04_lru_cache/cache_test.go b/hw04_lru_cache/cache_test.go index e99447d..fd3ea5c 100644 --- a/hw04_lru_cache/cache_test.go +++ b/hw04_lru_cache/cache_test.go @@ -50,7 +50,31 @@ func TestCache(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) }) } diff --git a/hw04_lru_cache/go.mod b/hw04_lru_cache/go.mod index b4abc63..ba531d5 100644 --- a/hw04_lru_cache/go.mod +++ b/hw04_lru_cache/go.mod @@ -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 diff --git a/hw04_lru_cache/list.go b/hw04_lru_cache/list.go index eab309f..8e7db93 100644 --- a/hw04_lru_cache/list.go +++ b/hw04_lru_cache/list.go @@ -1,17 +1,101 @@ package hw04_lru_cache //nolint:golint,stylecheck -type List interface { - // Place your code here +type ListInterface interface { + 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 { - // Place your code here +type ListItem struct { + Value interface{} // значение + Next *ListItem // следующий элемент + Prev *ListItem // предыдущий элемент } -type list struct { - // Place your code here +type List struct { + Info ListItem + len int } -func NewList() List { - return &list{} +func NewList() *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 } diff --git a/hw04_lru_cache/list_test.go b/hw04_lru_cache/list_test.go index 02109fd..a219aab 100644 --- a/hw04_lru_cache/list_test.go +++ b/hw04_lru_cache/list_test.go @@ -18,14 +18,50 @@ func TestList(t *testing.T) { t.Run("complex", func(t *testing.T) { l := NewList() - l.PushFront(10) // [10] - l.PushBack(20) // [10, 20] - l.PushBack(30) // [10, 20, 30] - require.Equal(t, l.Len(), 3) + l.PushFront(10) + require.Equal(t, []int{10}, transpond(l)) + require.Equal(t, 1, l.Len()) + + 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 - l.Remove(middle) // [10, 30] - require.Equal(t, l.Len(), 2) + l.Remove(middle) + 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} { if i%2 == 0 { @@ -33,19 +69,28 @@ func TestList(t *testing.T) { } else { l.PushBack(v) } - } // [80, 60, 40, 10, 30, 50, 70] - - require.Equal(t, l.Len(), 7) - require.Equal(t, 80, l.Front().Value) - require.Equal(t, 70, l.Back().Value) - - l.MoveToFront(l.Front()) // [80, 60, 40, 10, 30, 50, 70] - l.MoveToFront(l.Back()) // [70, 80, 60, 40, 10, 30, 50] - - elems := make([]int, 0, l.Len()) - for i := l.Back(); i != nil; i = i.Next { - elems = append(elems, i.Value.(int)) } - require.Equal(t, []int{50, 30, 10, 40, 60, 80, 70}, elems) + require.Equal(t, []int{70, 50, 30, 10, 20, 40, 60, 80}, transpond(l)) + require.Equal(t, 8, l.Len()) + + l.MoveToFront(l.Front()) + require.Equal(t, []int{70, 50, 30, 10, 20, 40, 60, 80}, transpond(l)) + require.Equal(t, 8, l.Len()) + + l.MoveToFront(l.Back().Next.Next) // 30 + 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()) + for i := l.Back(); i != nil; i = i.Next { + elems = append(elems, i.Value.(int)) + } + return elems +} From 11d4b357743479e0650a5efbaf5536c62f7b576d Mon Sep 17 00:00:00 2001 From: Andrey Ivanov Date: Tue, 23 Jun 2020 14:45:11 +0300 Subject: [PATCH 2/4] HW4 is completed --- hw04_lru_cache/cache.go | 26 +++++++++++++++++++------- hw04_lru_cache/cache_test.go | 7 +++---- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/hw04_lru_cache/cache.go b/hw04_lru_cache/cache.go index dd93cfd..1d8288e 100644 --- a/hw04_lru_cache/cache.go +++ b/hw04_lru_cache/cache.go @@ -1,4 +1,5 @@ package hw04_lru_cache //nolint:golint,stylecheck +import "sync" type Key string @@ -12,6 +13,7 @@ type lruCache struct { capacity int queue *List items map[Key]*ListItem + mx sync.RWMutex } type Item struct { @@ -23,25 +25,33 @@ func NewCache(capacity int) Cache { return &lruCache{ capacity: capacity, queue: NewList(), - items: map[Key]*ListItem{}, + items: make(map[Key]*ListItem), } } func (l *lruCache) Set(key Key, value interface{}) bool { - if _, exists := l.Get(key); exists { - l.items[key].Value = value + 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 { - // TODO: нужно как-то удалить элемент из мапа - //delete(l.items,l.queue.Back().(*Item).Key) + if l.queue.Len() == l.capacity { + l.mx.RLock() + delete(l.items, l.queue.Back().Value.(Item).Key) l.queue.Remove(l.queue.Back()) + l.mx.RUnlock() } - l.items[key] = l.queue.PushFront(value) + 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 } @@ -50,7 +60,9 @@ func (l *lruCache) Get(key Key) (interface{}, bool) { } func (l *lruCache) Clear() { + l.mx.Lock() l.items = nil l.queue.len = 0 l.queue.Info = ListItem{} + l.mx.Unlock() } diff --git a/hw04_lru_cache/cache_test.go b/hw04_lru_cache/cache_test.go index fd3ea5c..c16cf82 100644 --- a/hw04_lru_cache/cache_test.go +++ b/hw04_lru_cache/cache_test.go @@ -31,18 +31,18 @@ func TestCache(t *testing.T) { val, ok := c.Get("aaa") require.True(t, ok) - require.Equal(t, 100, val) + require.Equal(t, 100, val.(Item).Value) val, ok = c.Get("bbb") require.True(t, ok) - require.Equal(t, 200, val) + require.Equal(t, 200, val.(Item).Value) wasInCache = c.Set("aaa", 300) require.True(t, wasInCache) val, ok = c.Get("aaa") require.True(t, ok) - require.Equal(t, 300, val) + require.Equal(t, 300, val.(Item).Value) val, ok = c.Get("ccc") require.False(t, ok) @@ -79,7 +79,6 @@ func TestCache(t *testing.T) { } func TestCacheMultithreading(t *testing.T) { - t.Skip() // Remove if task with asterisk completed c := NewCache(10) wg := &sync.WaitGroup{} From 1db330e7123c08c9b7debcf05f4cc44cbb21db17 Mon Sep 17 00:00:00 2001 From: Andrey Ivanov Date: Tue, 23 Jun 2020 15:02:08 +0300 Subject: [PATCH 3/4] HW4 is completed --- hw04_lru_cache/cache.go | 2 +- hw04_lru_cache/cache_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw04_lru_cache/cache.go b/hw04_lru_cache/cache.go index 1d8288e..e6929af 100644 --- a/hw04_lru_cache/cache.go +++ b/hw04_lru_cache/cache.go @@ -56,7 +56,7 @@ func (l *lruCache) Get(key Key) (interface{}, bool) { return nil, false } l.queue.MoveToFront(l.items[key]) - return l.items[key].Value, true + return l.items[key].Value.(Item).Value, true } func (l *lruCache) Clear() { diff --git a/hw04_lru_cache/cache_test.go b/hw04_lru_cache/cache_test.go index c16cf82..09ab0bd 100644 --- a/hw04_lru_cache/cache_test.go +++ b/hw04_lru_cache/cache_test.go @@ -31,18 +31,18 @@ func TestCache(t *testing.T) { val, ok := c.Get("aaa") require.True(t, ok) - require.Equal(t, 100, val.(Item).Value) + require.Equal(t, 100, val) val, ok = c.Get("bbb") require.True(t, ok) - require.Equal(t, 200, val.(Item).Value) + require.Equal(t, 200, val) wasInCache = c.Set("aaa", 300) require.True(t, wasInCache) val, ok = c.Get("aaa") require.True(t, ok) - require.Equal(t, 300, val.(Item).Value) + require.Equal(t, 300, val) val, ok = c.Get("ccc") require.False(t, ok) From a161171e11b480bf7a1c7fa6261b23e25166c01f Mon Sep 17 00:00:00 2001 From: Andrey Ivanov Date: Wed, 24 Jun 2020 08:29:44 +0300 Subject: [PATCH 4/4] HW4 is completed --- hw04_lru_cache/cache.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/hw04_lru_cache/cache.go b/hw04_lru_cache/cache.go index e6929af..89ec238 100644 --- a/hw04_lru_cache/cache.go +++ b/hw04_lru_cache/cache.go @@ -1,5 +1,8 @@ package hw04_lru_cache //nolint:golint,stylecheck -import "sync" +import ( + "log" + "sync" +) type Key string @@ -39,7 +42,11 @@ func (l *lruCache) Set(key Key, value interface{}) bool { } if l.queue.Len() == l.capacity { l.mx.RLock() - delete(l.items, l.queue.Back().Value.(Item).Key) + 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() } @@ -56,7 +63,11 @@ func (l *lruCache) Get(key Key) (interface{}, bool) { return nil, false } l.queue.MoveToFront(l.items[key]) - return l.items[key].Value.(Item).Value, true + s, ok := l.items[key].Value.(Item) + if !ok { + log.Fatal("Ошибка приведения типов") + } + return s.Value, true } func (l *lruCache) Clear() {