Структури от данни

24.10.2018

Но преди това...

Ретроспекция на домашното

Почти! Сега само малко...

Въпрос за мъфин #1

Какво ще покаже следния код?

func 4times4() (res int) {
    defer func() {
        res *= 4
    }
    return 4
}
fmt.Println(4times4())

Въпрос за мъфин #2

С какво можем да сравняваме функции?

Въпрос за мъфин #3

Ако знаем, че foo е указател към int в масив, то как можем да вземем следващия елемент в масива?

Въпрос за мъфин #4

Кога се оценяват аргументите на defer функция?

Arrays

package main

import "fmt"

func main() {
    var x [5]float64
    x[0] = 98
    x[1] = 93
    x[2] = 77
    x[3] = 82
    x[4] = 83

    var total float64 = 0
    for i := 0; i < 5; i++ {
        total += x[i]
    }
    fmt.Println(total / 5)
}

Очевидно броим от 0

Инициализация

var x [5]string
x[0] = "Баба"
x[1] = "меца"
x[2] = "яде"
x[3] = "от"
x[4] = "медеца"

или накратко:

x := [6]float64{98, 93, 77, 82, 83}

Чакай малко! Подали сме само 5 числа.

x[5] == 0

Компилаторите могат да броят!

x := [...]string{"Incredible", "isn't", "it?"}

Сравняване на масиви

Да разгледаме следния код

package main

import "fmt"

func main() {
    dqdo := [4]uint32{0, 1, 2, 3}
    baba := [4]uint32{0, 1, 2, 3}

    fmt.Printf("The two arrays are identical: %t\n", dqdo == baba)
}

Големината на масив е част от типа му

package main

import "fmt"

func main() {
    dqdo := [4]uint32{0, 1, 2, 3}
    baba := [5]uint32{0, 1, 2, 3}

    fmt.Printf("The two arrays are identical: %t\n", dqdo == baba)
}

Черва на масивите

func (foo [100]uint32)
a := [100]float64
b := a
func (foo *[100]uint32)

Полезнотии

for index, value := range arr {
    ...
}



for index := 0; index < len(arr); index++ {
    value := arr[index]
    ...
}

Тези два цикъла са еквивалентни

(Заради range нямаме нужда от foreach)

Това е много готино, но...

Slices

Като масивите имат дължина и могат да се индексират, но дължината им може да се променя*.

var x []float64

Горното само създава променливата, а се инициализира по следния начин:

x := make([]float64, 5)

Това указва на слайса да бъде с размер 5. Всеки слайс е част от масив с не по-малка дължина от слайса.

x := make([]float64, 5, 10)

Това е същото като горното, но този слайс сочи към масив с размер 10.

Без изрично `make(...)`

numbers := []float64{0, 1.2, 3.4, 55.3}

Слайсове в действие

arr := [6]float64{1, 2, 3, 4, 5, 6}
x := arr[1:5]

Създаваме слайс от втори до пети елемент включително на масива arr.

x := arr[2:] // Взема всички без първите два елемента
x := arr[:2] // Взема първите два елемента
x := arr[:]  // Взема всички елементи

Не точно масиви

Структура

x := []int{2, 3, 5, 7, 11}

Създава нов slice, който сочи към нов масив от 5 елемента.

y := x[1:3]

Създава нов slice, но не и нов масив - използва се вече съществуващия за x.

Полезнотии

len и cap

package main

import "fmt"

func main() {
    x := []int{2, 3, 5, 7, 11}
    y := x[1:3]

    fmt.Println("len(x) =", len(x), ", cap(x) =", cap(x))
    fmt.Println("len(y) =", len(y), ", cap(y) =", cap(y))
}

Нулева стойност

var foo []uint32
foo == nil // True
len(foo) == cap(foo) == 0

Резултатни slice-ове

x := []uint32{0, 1, 2, 3, 4, 5, 6, 7}
y := x[:]
y[4] = 42
x[4] == 42  // True

`cap` - втори епизод

x := []uint32{0, 1, 2, 3, 4, 5, 6, 7}
y := x[2:4]  // [2, 3]
y = y[:cap(y)]  // [2, 3, 4, 5, 6, 7]

append

Built-in функция, която добавя елементи към края на slice:

sliceA := []int{1, 2, 3}
sliceB := append(sliceA, 4, 5) // [1 2 3 4 5]

Може да добавя и един slice към друг:

sliceC := append(sliceA, sliceB...)

Ако в резултатния slice има достатъчно място, той се използва непроменен. Ако няма, автоматично се заделя по-голям slice:

sliceD := make([]int, 0, 3)   // len = 0, cap = 3
sliceD = append(sliceD, 1, 2) // len = 2, cap = 3
sliceD = append(sliceD, 2, 4) // len = 4, cap = 6

Трик с append

Изтриване на n-ия елемент от слайс

x := []int{1, 2, 3, 4, 5}
x = append(x[:n], x[n+1:]...)

Ако n = 2:

[]int{1, 2, 4, 5}

copy(dst, src)

copy(dst, src) - примери

var l int
slice1 := []int{1, 2, 3, 4}
slice2 := []int{7, 6, 5}

Копираме трите елемента от slice2 в slice1

l = copy(slice1, slice2) // slice1 = [7 6 5 4], l = 3

Копираме края на slice1 в началото му

l = copy(slice1, slice1[2:]) // slice1 = [3 4 3 4], l = 2

Копираме slice1 в slice2

l = copy(slice2, slice1) // slice2 = [1 2 3], l = 3
// Копират се само първите 3 елемента, защото len(slice2) = 3

slice "gotchas"

1. Опит за писане в неинициализиран слайс води до паника.

2. Масивите, в които се съхраняват данните на слайсовете, не се чистят от garbage collector-a, докато има референции (слайсове) към тях.

// WARNING: shitty code, don't do this at home. We are professionals!
func GetFileHeader(filename string) []byte {
    b, _ := ioutil.ReadFile(filename)
    return b[:10]
}

Цялото съдържание на файла няма да бъде изчистено от паметта, докато първите 10 байта се ползват някъде.

Решение: copy() в нов слайс

Maps

Неподредена колекция от двойки ключове и стойности

var x map[string]int // Ключовете в x са низове, а стойностите числа

За да го инициализраме, ползваме make:

x := make(map[string]int)

Подобно на слайсовете, писането в неинициализиран map води до паника.

Ползваме го почти както масиви и слайсове. Добавяне на стойност:

x["key"] = 10

За да вземем стойност по ключ:

value, ok := x["key"]

ok е true, ако съществува двойка с такъв ключ. В противен случай, value е нулевата стойност на типа ("" за string) и ok е false.

Полезнотии

wordcount := map[string]int{"word1": 10, "word2": 5}
x := make(map[string]int)
delete(x, "key") // Изтрива двойката с ключ е "key". Ако няма такава, нищо не се случва.
if _, ok := x["key"]; ok {
    fmt.Println("key exists")
}
for key, value := range m {
    fmt.Println("Key:", key, "Value:", value)
}

Ключове

Сравняване?

Конкурентен достъп

Конкурентен достъп (2)

Но има sync.Map в стандартната библиотека, който позволява конкурентен достъп.

Structs

type Person struct {
    name string
    age uint
}

var chochko Person
chochko.name = "Чочко"
chochko.age = 27

Други начини за инициализиране:

chochko := Person{name: "Чочко", age: 27}
chochko := Person{"Чочко", 27}

new()

chochko := new(Person)
chochko.name = "Чочко"
chochko.age = 27

new() vs. make()

new само заделя и нулира памет, а make инициализира, т.е.:

package main

import "fmt"

type example struct {
    attrs map[string]int
}

func main() {
    e := new(example)
    e.attrs = make(map[string]int)
    e.attrs["h"] = 42
    fmt.Println(e)
}

Следващия път

Въпроси?