Трета задача

  1. Функцията Contains няма как да гарантираме дали ще работи коректно освен ако нямаме метод който ни локва мап-а понеже няма как да гарантирам че при излизане от метод времето на ключа няма да изтече. Тоест вижда ми се малко безсмислен понеже нямам как да разчитаме на това което ни връща той това важи и за функцията Size

  2. @Стоян, обаче беше прекалено трудно да отвориш форума и да видиш тази тема, нали? И може би ако беше присъствал в четвъртъците (не във вторниците, в които си зает), щеше да знаеш, че:

    1. Ще имаме тест (обявихме го седмица по-рано и в четвъртъка преди теста напомнихме).
    2. Можеш да говориш с нас през почивките и след лекцията, а не да ни пишеш по "смотания мейл", когато ти е толкова важно.
  3. И причината за всичко това е, че мейлът ти беше в спама. Ще ти върна отговор тези дни. Не оспорваме това, а начина по който ни пингваш ;)

    Супер. Хайде сега обратно на задачката, дето сме ви дали :)

  4. Не ми е много ясно какво имате предвид под:

    Когато стойността е от тип, различен от тези неща 
    функцията трябва да върне грешка, 
    различна от nil. float32 и float64 не са целочислен типове.
    

    (относно функцията Increment)

    Малко разяснение, ако може?

  5. @Цветелина разбирам! Явно трябва да е от вида:

    -тип- не е целочислен тип!

    Като гледам в условието, което са ни дали, не пише изрично как трябва да изглежда error msg. Няма проверка и в тестовете.

    Остава да изглежда нещо от сорта на това горното, дори при интересно изглеждащото:

    bool не е целочислен тип!

    Ама ми е неясно дали действително искат да е в тази форма, или друга...

  6. Имам няколко въпроса - сигурно с очевидни отговори, но все пак "better safe than sorry".

    1. Добавя в хранилището нов ключ 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 секунди?

    2. За ключ увеличава стойността му числово с 1 когато тази стойност е от тип int или стринг, представящ цяло число. Когато стойността е от тип, различен от тези неща функцията трябва да върне грешка, различна от nil. float32 и float64 не са целочислен типове. Времето на изтичане на ключа не трябва да се променя.

      Когато такъв ключ не съществува, то и стойността му няма да съществува - тогава трябва ли да връщаме някаква грешка? (изглежда ми, че не трябва)

    3. Ок ли е типът на грешката ни да бъде публичен или се иска да се вижда само ExpireMap и то само с методите, дадени в задачата?

  7. @Мартин, няма да ни пречат тези дебъг логове, но на твое място бих ги махнал преди да си предам домашното. Не за друго, ами от чиста пригледност.

    @Илия:

    1. mp.Set би трябвало напълно да презапише предишния ключ => стойността трябва да е "b" и да изтече 10 секунди след изпълнението на втория mp.Set.
    2. Трябва да върне грешка, че няма такъв ключ.
    3. Не ни интересува каква точно е грешката. Просто трябва да е някакъв error. Аз бих си дефинирал мои типове, но е напълно в реда на нещата да връщате и неща като errors.New("No such key"). Това отговаря и на въпроса на @Ясен :)

    edit: Упс, Мишо вече е отговорил на тези (може би трябва да рефрешвам, преди да пращам отговора). Все пак няма да го трия. Да си личи как целият екип е наясно с идеята на задачата :D

  8. Имам само един тест който е за "бонуса" за не харчене на памет на изтеклите стойностти. Аз не го минавам.

    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)
            }
        }
    }
    

    *Копирам го тук понеже е кратичък

  9. Някой да има идея, това как се оправя: invalid operation: em[key] (type *ExpireMap does not support indexing). Предполагам е нещо супер глупаво, но в момента съм забил. Между другото пробвах се да пратя като домашно коментар със secret gist, за да попитам и НЕ СТАВА.

  10. Проблемът ти е, че *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-а като анонимен атрибут, но пак не се получи съпорта за индексиране.

  11. @Йончо и @Илия,

    Погледнете преработения код на Илия който съм постнал в play.golang.org (може да го ползвате за в бъдеще с цел бързо тестване и показване на нещо).

    1. @Илия да не би да мислиш че new(TempMap) вика NewTestMap ? защото това не е така - предполагам че си писал набързо и не си се усетил че не викаш функцията си а викаш new.

    2. краткия отговор на въпроса е "защото *map няма индексиране."

    3. Както съм показал в примера стига да го дереферираш до map или още по-добре да не използваш указател вслучая - всичко е минава.

    Ползването на указател вслучая не е пропоръчително по същата причина поради коятo не е препоръчително и за канали, слайсове и интефейси. Причината е че те самите вече са (или по-точно съдържат) референция(указател) към дадена друга вътрешна структура - съответно обикновенно не се спестява някакво особенно количество копиране и още по важно go не имплементира същите операции върху указатели към тях както към самите тях - нито индексирането на *[]int работи нито може да направите ch <- 2 ако ch e *chan int нито може да извикате метод на даден interface върху указател от дадения interface

    Не знам дали съм достатъчно ясен .. ако има още нещо не ясно питайте.

Трябва да сте влезли в системата, за да може да отговаряте на теми.