Тук е мястото да питате, ако има нещо неясно по условието на трета задача. Примерният тест е тук.
Трета задача
Offtopic: Преглеждайте си мейла (fmi@golang.bg)
Функцията Contains няма как да гарантираме дали ще работи коректно освен ако нямаме метод който ни локва мап-а понеже няма как да гарантирам че при излизане от метод времето на ключа няма да изтече. Тоест вижда ми се малко безсмислен понеже нямам как да разчитаме на това което ни връща той това важи и за функцията Size
@Стоян - гледаме го ... ти пращал ли си нещо?
@Мартин - приеми че ни интересува към момента на извикване, а не към момента на връщане, ако това ти помага.
@Стояне, сериозно ли реши, че това е най-подходящото място за това!?
@Кирил Да тук реших... Като не си четете смотания мейл и няма нормален начин за връзка с екипа освен него :).
Послепис: Писах ти и на скайпа, но и него не гледаш.
@Михаил, Два пъти писах :). Ако е по-удобно дай личен адрес
@Стоян, обаче беше прекалено трудно да отвориш форума и да видиш тази тема, нали? И може би ако беше присъствал в четвъртъците (не във вторниците, в които си зает), щеше да знаеш, че:
- Ще имаме тест (обявихме го седмица по-рано и в четвъртъка преди теста напомнихме).
- Можеш да говориш с нас през почивките и след лекцията, а не да ни пишеш по "смотания мейл", когато ти е толкова важно.
И причината за всичко това е, че мейлът ти беше в спама. Ще ти върна отговор тези дни. Не оспорваме това, а начина по който ни пингваш ;)
Супер. Хайде сега обратно на задачката, дето сме ви дали :)
Какво очакваме да се случи при следния сценарий:
obekt.Set("_!_", black, time.Millisecond * 200) time.Sleep(100*time.Millisecond) obekt.Set("_!_", chineese, time.Millisecond * 21456)
Не ми е много ясно какво имате предвид под:
Когато стойността е от тип, различен от тези неща функцията трябва да върне грешка, различна от nil. float32 и float64 не са целочислен типове.
(относно функцията
Increment
)Малко разяснение, ако може?
@Ясен, и аз се обърках, но виж, че Increment връща error, т.е. трябва да си направим наша custom грешка като SusiError например в предното домашно. :)
@Цветелина разбирам! Явно трябва да е от вида:
-тип- не е целочислен тип!
Като гледам в условието, което са ни дали, не пише изрично как трябва да изглежда error msg. Няма проверка и в тестовете.
Остава да изглежда нещо от сорта на това горното, дори при интересно изглеждащото:
bool не е целочислен тип!
Ама ми е неясно дали действително искат да е в тази форма, или друга...
- @Ясен, ако не се лъжа, достатъчно е
err
да не еnil
. - @Иван, ще презапишеш стойността и в сила ще е втория expire.
- @Ясен, ако не се лъжа, достатъчно е
А можем ли да си сложим произволно количество дебъг логове които да ни участват и в крайното решение
На когото му е интересно - или трудно схване тънкостите на
chan
, да види тук: http://blog.golang.org/pipelinesИмам няколко въпроса - сигурно с очевидни отговори, но все пак "better safe than sorry".
-
Добавя в хранилището нов ключ key със стойност value за expire време. Duration е дефиниран в пакета time. expire време след добавянето на ключа той вече трябва да спре да бъде в хранилището.
"expire време след добавянето на ключа той вече трябва да спре да бъде в хранилището". Това ми изглежда, че трябва да трие този ключ независимо от това дали е бил презаписан. Тоест тук:
mp.Set("a", "a", 5 * time.Second) mp.Set("a", "b", 10 * time.Second) // извикано веднага след това
Стриктно погледнато моето разбиране е, че би трябвало 5 секунди след началото да изтрие новата стойност. Силно ме съмнява вие да сте искали да кажете това. Тоест първият ми въпрос е в случая колко време искате да съществува стойността
"b"
- 5 или 10 секунди? -
За ключ увеличава стойността му числово с 1 когато тази стойност е от тип int или стринг, представящ цяло число. Когато стойността е от тип, различен от тези неща функцията трябва да върне грешка, различна от nil. float32 и float64 не са целочислен типове. Времето на изтичане на ключа не трябва да се променя.
Когато такъв ключ не съществува, то и стойността му няма да съществува - тогава трябва ли да връщаме някаква грешка? (изглежда ми, че не трябва)
Ок ли е типът на грешката ни да бъде публичен или се иска да се вижда само ExpireMap и то само с методите, дадени в задачата?
-
- Ключа трябва да изтече след 10 секунди.
- Разбира се че трябва да върне грешка.
- Да.
@Мартин, няма да ни пречат тези дебъг логове, но на твое място бих ги махнал преди да си предам домашното. Не за друго, ами от чиста пригледност.
@Илия:
-
mp.Set
би трябвало напълно да презапише предишния ключ => стойността трябва да е "b" и да изтече 10 секунди след изпълнението на вторияmp.Set
. - Трябва да върне грешка, че няма такъв ключ.
- Не ни интересува каква точно е грешката. Просто трябва да е някакъв
error
. Аз бих си дефинирал мои типове, но е напълно в реда на нещата да връщате и неща катоerrors.New("No such key")
. Това отговаря и на въпроса на @Ясен :)
edit: Упс, Мишо вече е отговорил на тези (може би трябва да рефрешвам, преди да пращам отговора). Все пак няма да го трия. Да си личи как целият екип е наясно с идеята на задачата :D
-
Ето малко тестове.
Ако някой друг също сподели би било приятно
Имам само един тест който е за "бонуса" за не харчене на памет на изтеклите стойностти. Аз не го минавам.
func TestМем(t *testing.T) { m := NewExpireMap() var top int = 1e7 got := 0 for c := 0; c < top; c++ { m.Set(fmt.Sprintf("%d", c), c, time.Millisecond*1) } for c := 0; c < top; c++ { select { case <-m.ExpiredChan(): got++ case <-time.After(time.Millisecond + 10*time.Millisecond): t.Fatalf("Timout before all keys have expired - remaining: [%d]", top-got) } } }
*Копирам го тук понеже е кратичък
Когато ползваме toUpper и toLower трябва ли да обновим времето на ключа ?
Очевидно не, промянта е само в ключа. Гледай на мап-а като кошница с ябълки с етикет и срок на годност. Това просто сменя етикета.
@Илия, благодаря за тестовете, но на ред 209 ми се оплаква, че
ok
е декларирано, но не е използванода - благодаря - аз си го бях оправил при себе си, но в github не го бях обновил. благодаря - вече е оправено.
Някой да има идея, това как се оправя: invalid operation: em[key] (type *ExpireMap does not support indexing). Предполагам е нещо супер глупаво, но в момента съм забил. Между другото пробвах се да пратя като домашно коментар със secret gist, за да попитам и НЕ СТАВА.
@Йончо, опитваш се да итерираш директно през собствения си тип, а не през мапа, който се намира в него?
Не, опивам се да достъпя елемент по ключ: type ExpireMap map[string]Entry ми е структурата.
Проблемът ти е, че
*ExpireMap
няма индексиране, а самоExpireMap
(не знам защо).Ако си направиш make вместо
new
всичко ще работи, но това ще е простоmap
. Пробвах да се върне*map
вNewTempMap
, но проблемът пак е налице.*ExpireMap
няма индексиране и затова не тръгва. Ако вземеш*em
(type ExpireMap)
пък се компилира, но после се панира сruntime error: assignment to entry in nil map
.Предполагам, че понеже това може да се разгледа като по-генерален въпрос няма проблем да отговоря и с код:
type TempMap map[string]int func NewTestMap() *TempMap { m := make(TempMap) return &m } func (tm TempMap) set(key string, value int) { tm[key] = value } func TestTempMap1(t *testing.T) { tm := make(TempMap) tm["a"] = 5 // works } func TestTempMap2(t *testing.T) { tm := new(TempMap) // returns an *ExpireMap // tm["a"] = 5 // (type *ExpireMap does not support indexing) tm.set("a", 5) // panic: runtime error: assignment to entry in nil map }
Кратка версия не съм сигурен как може да стане. Очевидно ти трябва стойността на
ExpireMap
, но когато се взима дава, че еnil map
. Тоест явно още правенето на&m
не е много безопасно. Ако погледнеш примерите, които се пишат в час никъде не използват тип*map[string]___
, а самоmap[string]___
.Тоест явно go иска да работим с map, а не
*map
- иначе сигурно и make щеше да връща*map
The built-in function make takes a type T, which must be a slice, map or channel type, optionally followed by a type-specific list of expressions. It returns a value of type T (not *T).
Пробвах и да вградя
map
-а като анонимен атрибут, но пак не се получи съпорта за индексиране.@Йончо и @Илия,
Погледнете преработения код на Илия който съм постнал в play.golang.org (може да го ползвате за в бъдеще с цел бързо тестване и показване на нещо).
@Илия да не би да мислиш че
new(TempMap)
вика NewTestMap ? защото това не е така - предполагам че си писал набързо и не си се усетил че не викаш функцията си а викаш new.краткия отговор на въпроса е "защото *map няма индексиране."
- Както съм показал в примера стига да го дереферираш до map или още по-добре да не използваш указател вслучая - всичко е минава.
Ползването на указател вслучая не е пропоръчително по същата причина поради коятo не е препоръчително и за канали, слайсове и интефейси. Причината е че те самите вече са (или по-точно съдържат) референция(указател) към дадена друга вътрешна структура - съответно обикновенно не се спестява някакво особенно количество копиране и още по важно go не имплементира същите операции върху указатели към тях както към самите тях - нито индексирането на
*[]int
работи нито може да направитеch <- 2
акоch
e*chan int
нито може да извикате метод на даден interface върху указател от дадения interfaceНе знам дали съм достатъчно ясен .. ако има още нещо не ясно питайте.
Някакъв шанс да получа тестовете, които сте рънвали срещу домашните?
Трябва да сте влезли в системата, за да може да отговаряте на теми.