Решение на ExpireMap от Любомир Райков

Обратно към всички решения

Към профила на Любомир Райков

Резултати

  • 7 точки от тестове
  • 0 бонус точки
  • 7 точки общо
  • 18 успешни тест(а)
  • 7 неуспешни тест(а)

Код

package main
import (
"strconv"
"strings"
"time"
)
type ExpireMap struct {
innerMap map[string]expireValue
expiredChan chan string
isDestroyed bool
}
type ExpireMapError struct {
errorMessage string
}
func (emr ExpireMapError) Error() string {
return emr.errorMessage
}
type expireValue struct {
value interface{}
expireDuration time.Duration
expireTime time.Time
}
func NewExpireMap() *ExpireMap {
e := new(ExpireMap)
e.innerMap = make(map[string]expireValue)
e.expiredChan = make(chan string)
return e
}
func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
keyValue := new(expireValue)
keyValue.value = value
keyValue.expireDuration = expire
keyValue.expireTime = time.Now().Add(expire)
em.innerMap[key] = *keyValue
go func(expire time.Duration) {
select {
case <-time.After(expire):
if em.Contains(key) {
go func() {
if em.isDestroyed {
return
}
em.expiredChan <- key
}()
em.Delete(key)
}
}
}(expire)
}
func (em *ExpireMap) Get(key string) (interface{}, bool) {
value, ok := em.innerMap[key]
return value.value, ok
}
func (em *ExpireMap) GetInt(key string) (int, bool) {
if val, ok := em.innerMap[key].value.(int); ok {
return val, ok
}
return 0, false
}
func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
if val, ok := em.innerMap[key].value.(float64); ok {
return val, ok
}
return 0, false
}
func (em *ExpireMap) GetString(key string) (string, bool) {
if val, ok := em.innerMap[key].value.(string); ok {
return val, ok
}
return "", false
}
func (em *ExpireMap) GetBool(key string) (bool, bool) {
if val, ok := em.innerMap[key].value.(bool); ok {
return val, ok
}
return false, false
}
func (em *ExpireMap) Expires(key string) (time.Time, bool) {
if value, ok := em.innerMap[key]; ok {
return value.expireTime, ok
}
return *new(time.Time), false
}
func (em *ExpireMap) Delete(key string) {
if em.Contains(key) {
delete(em.innerMap, key)
}
}
func (em *ExpireMap) Contains(key string) bool {
_, ok := em.innerMap[key]
return ok
}
func (em *ExpireMap) Size() int {
return len(em.innerMap)
}
func (em *ExpireMap) Increment(key string) error {
return em.changeIntValue(key, func(num int) int {
return num + 1
})
}
func (em *ExpireMap) Decrement(key string) error {
return em.changeIntValue(key, func(num int) int {
return num - 1
})
}
func (em *ExpireMap) changeIntValue(key string, changeFunction func(value int) int) error {
if num, ok := em.GetInt(key); ok {
tmp := em.innerMap[key]
tmp.value = changeFunction(num)
em.innerMap[key] = tmp
return nil
}
if str, ok := em.GetString(key); ok {
if numValue, ok := strconv.Atoi(str); ok == nil {
numValue = changeFunction(numValue)
tmp := em.innerMap[key]
tmp.value = strconv.Itoa(numValue)
em.innerMap[key] = tmp
return nil
}
}
return ExpireMapError{
errorMessage: "Error when changing integer value",
}
}
func (em *ExpireMap) changeStringValue(key string, changeFunction func(value string) string) error {
if str, ok := em.GetString(key); ok {
str = changeFunction(str)
tmp := em.innerMap[key]
tmp.value = str
em.innerMap[key] = tmp
return nil
}
return ExpireMapError{
errorMessage: "Error when changing string value",
}
}
func (em *ExpireMap) ToUpper(key string) error {
return em.changeStringValue(key, func(str string) string {
return strings.ToUpper(str)
})
}
func (em *ExpireMap) ToLower(key string) error {
return em.changeStringValue(key, func(str string) string {
return strings.ToLower(str)
})
}
func (em *ExpireMap) ExpiredChan() <-chan string {
return em.expiredChan
}
func (em *ExpireMap) Cleanup() {
for key, _ := range em.innerMap {
em.Delete(key)
}
}
func (em *ExpireMap) Destroy() {
em.Cleanup()
close(em.expiredChan)
em.isDestroyed = true
}

Лог от изпълнението

PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
panic: test timed out

goroutine 15 [running]:
testing.alarm()
	/usr/local/lib/go/src/pkg/testing/testing.go:533 +0x44
created by time.goFunc
	/usr/local/lib/go/src/pkg/time/sleep.go:122 +0x45

goroutine 1 [chan receive]:
testing.RunTests(0x8148e40, 0x81c3ba0, 0x19, 0x19, 0x1, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:434 +0x69f
testing.Main(0x8148e40, 0x81c3ba0, 0x19, 0x19, 0x81c6600, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:365 +0x69
main.main()
	_/tmp/d20141204-6466-11apmql/_test/_testmain.go:91 +0x81

goroutine 4 [sleep]:
time.Sleep(0x3c336080, 0x0)
	/usr/local/lib/go/src/pkg/runtime/ztime_linux_386.c:19 +0x3a
_/tmp/d20141204-6466-11apmql.TestSizes(0x183611e0)
	/tmp/d20141204-6466-11apmql/solution_test.go:111 +0x399
testing.tRunner(0x183611e0, 0x81c3bb8)
	/usr/local/lib/go/src/pkg/testing/testing.go:353 +0x87
created by testing.RunTests
	/usr/local/lib/go/src/pkg/testing/testing.go:433 +0x684

goroutine 5 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x3b9aca00, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f

goroutine 6 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x3b9aca00, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f

goroutine 7 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x3b9aca00, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f

goroutine 8 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x3b9aca00, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f

goroutine 9 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x3b9aca00, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f

goroutine 10 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x3b9aca00, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f

goroutine 11 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x3b9aca00, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f

goroutine 12 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x3b9aca00, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f

goroutine 13 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x3b9aca00, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f

goroutine 14 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x3b9aca00, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f
exit status 2
FAIL	_/tmp/d20141204-6466-11apmql	1.013s
panic: runtime error: send on closed channel

goroutine 7 [running]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 1 [runnable]:
testing.RunTests(0x8148e40, 0x81c3ba0, 0x19, 0x19, 0x1, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:434 +0x69f
testing.Main(0x8148e40, 0x81c3ba0, 0x19, 0x19, 0x81c6600, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:365 +0x69
main.main()
	_/tmp/d20141204-6466-11apmql/_test/_testmain.go:91 +0x81

goroutine 5 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x3b9aca00, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f
exit status 2
FAIL	_/tmp/d20141204-6466-11apmql	0.122s
panic: runtime error: send on closed channel

goroutine 7PASS
 [running]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 1 [panicwait]:

goroutine 6 [chan receive]:
_/tmp/d20141204-6466-11apmql.func·002(0x11e1a300, 0x0)
	/tmp/d20141204-6466-11apmql/solution.go:44 +0x66
created by _/tmp/d20141204-6466-11apmql.(*ExpireMap).Set
	/tmp/d20141204-6466-11apmql/solution.go:55 +0x16f
exit status 2
FAIL	_/tmp/d20141204-6466-11apmql	0.172s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.013s
--- FAIL: TestIncAndDecInManyRoutines-2 (0.01 seconds)
	solution_test.go:388: Expected 666 but found 2666
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-11apmql	0.026s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.112s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.062s
--- FAIL: TestExpiredChanWhenNoOneIsReading-2 (0.06 seconds)
	solution_test.go:554: Wrong key expired
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-11apmql	0.072s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.112s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.132s
--- FAIL: TestConcurrentOperations-2 (0.01 seconds)
	solution_test.go:728: Expected `A Б` but found `TIGER`
FAIL
exit status 1
FAIL	_/tmp/d20141204-6466-11apmql	0.024s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s
panic: runtime error: send on closed channel

goroutine 21 [running]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 1 [runnable]:
testing.RunTests(0x8148e40, 0x81c3ba0, 0x19, 0x19, 0x1, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:434 +0x69f
testing.Main(0x8148e40, 0x81c3ba0, 0x19, 0x19, 0x81c6600, ...)
	/usr/local/lib/go/src/pkg/testing/testing.go:365 +0x69
main.main()
	_/tmp/d20141204-6466-11apmql/_test/_testmain.go:91 +0x81

goroutine 34 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 33 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 32 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 30 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 29 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 28 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 27 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 24 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 22 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 26 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 25 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 31 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 23 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9

goroutine 20 [runnable]:
_/tmp/d20141204-6466-11apmql.func·001()
	/tmp/d20141204-6466-11apmql/solution.go:50 +0x4d
created by _/tmp/d20141204-6466-11apmql.func·002
	/tmp/d20141204-6466-11apmql/solution.go:51 +0xd9
exit status 2
FAIL	_/tmp/d20141204-6466-11apmql	0.273s
PASS
ok  	_/tmp/d20141204-6466-11apmql	0.012s

История (1 версия и 1 коментар)

Любомир обнови решението на 16.11.2014 20:58 (преди над 3 години)

+package main
+
+import (
+ "strconv"
+ "strings"
+ "time"
+)
+
+type ExpireMap struct {
+ innerMap map[string]expireValue
+ expiredChan chan string
+ isDestroyed bool
+}
+
+type ExpireMapError struct {
+ errorMessage string
+}
+
+func (emr ExpireMapError) Error() string {
+ return emr.errorMessage
+}
+
+type expireValue struct {
+ value interface{}
+ expireDuration time.Duration
+ expireTime time.Time
+}
+
+func NewExpireMap() *ExpireMap {
+ e := new(ExpireMap)
+ e.innerMap = make(map[string]expireValue)
+ e.expiredChan = make(chan string)
+ return e
+}
+
+func (em *ExpireMap) Set(key string, value interface{}, expire time.Duration) {
+ keyValue := new(expireValue)
+ keyValue.value = value
+ keyValue.expireDuration = expire
+ keyValue.expireTime = time.Now().Add(expire)
+ em.innerMap[key] = *keyValue
+ go func(expire time.Duration) {
+ select {
+ case <-time.After(expire):
+ if em.Contains(key) {
+ go func() {
+ if em.isDestroyed {
+ return
+ }
+ em.expiredChan <- key
+ }()
+ em.Delete(key)
+ }
+ }
+ }(expire)
+}
+
+func (em *ExpireMap) Get(key string) (interface{}, bool) {
+ value, ok := em.innerMap[key]
+ return value.value, ok
+}
+
+func (em *ExpireMap) GetInt(key string) (int, bool) {
+ if val, ok := em.innerMap[key].value.(int); ok {
+ return val, ok
+ }
+ return 0, false
+}
+
+func (em *ExpireMap) GetFloat64(key string) (float64, bool) {
+ if val, ok := em.innerMap[key].value.(float64); ok {
+ return val, ok
+ }
+ return 0, false
+}
+
+func (em *ExpireMap) GetString(key string) (string, bool) {
+ if val, ok := em.innerMap[key].value.(string); ok {
+ return val, ok
+ }
+ return "", false
+}
+
+func (em *ExpireMap) GetBool(key string) (bool, bool) {
+ if val, ok := em.innerMap[key].value.(bool); ok {
+ return val, ok
+ }
+ return false, false
+}
+
+func (em *ExpireMap) Expires(key string) (time.Time, bool) {
+ if value, ok := em.innerMap[key]; ok {
+ return value.expireTime, ok
+ }
+ return *new(time.Time), false
+}
+
+func (em *ExpireMap) Delete(key string) {
+ if em.Contains(key) {
+ delete(em.innerMap, key)
+ }
+}
+
+func (em *ExpireMap) Contains(key string) bool {
+ _, ok := em.innerMap[key]
+ return ok
+}
+
+func (em *ExpireMap) Size() int {
+ return len(em.innerMap)
+}
+
+func (em *ExpireMap) Increment(key string) error {
+ return em.changeIntValue(key, func(num int) int {
+ return num + 1
+ })
+}
+
+func (em *ExpireMap) Decrement(key string) error {
+ return em.changeIntValue(key, func(num int) int {
+ return num - 1
+ })
+}
+
+func (em *ExpireMap) changeIntValue(key string, changeFunction func(value int) int) error {
+ if num, ok := em.GetInt(key); ok {
+ tmp := em.innerMap[key]
+ tmp.value = changeFunction(num)
+ em.innerMap[key] = tmp
+ return nil
+ }
+ if str, ok := em.GetString(key); ok {
+ if numValue, ok := strconv.Atoi(str); ok == nil {
+ numValue = changeFunction(numValue)
+ tmp := em.innerMap[key]
+ tmp.value = strconv.Itoa(numValue)
+ em.innerMap[key] = tmp
+ return nil
+ }
+ }
+ return ExpireMapError{
+ errorMessage: "Error when changing integer value",
+ }
+}
+
+func (em *ExpireMap) changeStringValue(key string, changeFunction func(value string) string) error {
+ if str, ok := em.GetString(key); ok {
+ str = changeFunction(str)
+ tmp := em.innerMap[key]
+ tmp.value = str
+ em.innerMap[key] = tmp
+ return nil
+ }
+ return ExpireMapError{
+ errorMessage: "Error when changing string value",
+ }
+}
+
+func (em *ExpireMap) ToUpper(key string) error {
+ return em.changeStringValue(key, func(str string) string {
+ return strings.ToUpper(str)
+ })
+}
+
+func (em *ExpireMap) ToLower(key string) error {
+ return em.changeStringValue(key, func(str string) string {
+ return strings.ToLower(str)
+ })
+}
+
+func (em *ExpireMap) ExpiredChan() <-chan string {
+ return em.expiredChan
+}
+
+func (em *ExpireMap) Cleanup() {
+ for key, _ := range em.innerMap {
+ em.Delete(key)
+ }
+}
+
+func (em *ExpireMap) Destroy() {
+ em.Cleanup()
+ close(em.expiredChan)
+ em.isDestroyed = true
+}

Навярно още неща но това е може би най-важното :

em.Set("foo", 5, time.Millisecond)
em.Destroy()
time.Sleep(time.Second)
// panic here 

В допълнение не е задължително някой да слуша на ExpiredChan-а, както и не очакваме че ако никой не слуша и изтекът 10 ключа, като почне ще получи че са изтекли - очакваме обратното.

п.п. change* функциите ми харесват и принципно има type switch но на теб май не ти трябва