Функции и указатели
        20.10.2015
        
        
          
            
          
        
          
            
          
        
      
  
  
      
      
        Но преди това...
      
      
  
  
  
      
      
        Въпрос за мъфин #1
        
  
  
    Коя е последната стабилна версия на Go?
  
  
  
      
      
  
  
  
      
      
        Въпрос за мъфин #2
        
  
  
    Какъв трябва да е първия ред код в Go?
  
  
  
      
      
  
  
  
      
      
        Въпрос за мъфин #3
        
  
  
    Какво му е специалното на пакета main?
  
  
  
  
    - при компилацията му трябва да има функция main
 
  
    - ще направи приложение(изпълним файл) от пакета вместо просто библиотека
 
  
  
      
      
  
  
  
      
      
        Въпрос за мъфин #4
        
  
  
    Имате два пакета. Искате от единия да извикате функция в другия - какво е необходимо?
  
  
  
  
    - да си import-нете пакета
 
  
    - функцията да започва с главна буква
 
  
  
      
      
  
  
  
      
      
        Въпрос за мъфин #5
        
  
  
    iota e коя подред буква в гръцката азбука?
  
  
  
  
    - няма значение(приемлив отговор е)
 
  
    - деветата
 
  
  
      
      
  
  
  
      
      
        Въпрос за мъфин #6
        
  
  
    Какво трябва да направите преди да качите домашно/предизвикателство?
  
  
  
      
      
  
  
  
      
      
        Funcs 101
        
  
  
    - Функциите са основна структурна единица в Go
 
  
    - Всеки проблем, който някога ще решавате, ще е разбит на функции
 
  
    - Една функция върши точно едно нещо
 
  
    - ... в противен случай правите нещо грешно
 
  
    - Може да връща няколко резултата
 
  
    - Последните три точки не си противоречат взаимно
 
  
    - DRY
 
  
    - Редът, в който са дефинирани, не е от значение
 
  
    - Може и да изпада в паника (буквално)
 
  
  
      
      
  
  
  
      
      
        С func могат да се създават три неща
        
  
  
    1. Функции
  
  
  
  
    2. Ламбди
  
  
  
  
    3. Методи
  
  
      
      
  
  
  
      
      
        Аргументи
        
  
  func fоо(a int, b string) float64
 
  
  
  
    Функцията foo приема int и string и връща float64
  
  
  
  
    - Винаги работим върху копия от аргументите
 
  
    - Ако това не ни устройва, подаваме указател (за тях след малко)
 
  
  
      
      
  
  
  
      
      
        Когато няколко аргумента са от един тип
        
  
  func bar(a, b int, c float64) float64
 
  
      
      
  
  
  
      
      
        Произволен брой аргументи
        
  
  func sum(args ...int) int
 
  
  
  
    Функцията sum приема произволен брой числа и връща техния сбор
  
  
  
func sum(args ...int) int {
    result := 0
    for _, v := range args {
        result += v
    }
    return result
}
 
  
  
    Извикваме я с колкото ни трябват
  
  
  
  sum()           //0
sum(2, 3)       //5
sum(2, 3, 4, 5) //14
 
  
  
  
    Трябва да е последния аргумент на функцията
  
  
      
      
  
  
  
      
      
        Множество стойности като резултат
        
  
package main
import "fmt"
func main() {
	fmt.Println("Резултатът от sumAndCount(2, 3, 4, 5) е")
	fmt.Println(sumAndCount(2, 3, 4, 5))
}
func sumAndCount(args ...int) (int, int) {
    result := 0
    for _, v := range args {
        result += v
    }
    return result, len(args)
}
 
      
      
  
  
  
      
      
        Защо?
        
  
  
    - Няма нуждата от някои грозни C идиоми, като ...
 
  
    - ... модифициране на аргумент, подаден по адрес
 
  
    - ... errno
 
  
    - По-лесно справяне с грешки
 
  
  
      
      
  
  
  
      
      
        Как е реализирано в "стари" езици:
        
  
  
    - C/C++ и компания: масив или структура
 
  
    - Python: tuple-и
 
  
  
      
      
  
  
  
      
      
        Как е реализирано в Go?
        
  
  
    - Много просто: връщате каквото ви трябва
 
  
  
      
      
  
  
  
      
      
        Ами ако не ни трябват всичките резултати?
        
  
  
    1. Знаем, че ако дефинираме променлива и не я използваме, гърми
    2. Ако искаме онзи сбор и не ни интересува броят аргументи, това ще изгърми
  
  
  
  result, count := sumAndCount(2, 3, 4, 5)
 
  
  
  
    3. Ако нямаме нужда от дадена стойност, я присвояваме на _:
  
  
  
  result, _ := sumAndCount(2, 3, 4, 5)
 
  
  
  
    - Тя не се запазва и не можем да я достъпим след това
 
  
    - По-полезно е отколкото ви се струва
 
  
  
      
      
  
  
  
      
      
        Именовани резултати
        
  
func sumAndCount(args ...int) (result int, count int) {
    count = len(args)
    for _, v := range args {
        result += v
    }
    return
}
 
  
  
    - Резултатите се инициализират преди изпълнението на функцията
 
  
    - Добра идея са, ако пишем често в тях по време на изпълнение
 
  
    - Няма нужда да ги указваме при return
 
  
  
      
      
  
  
  
      
      
        Фунцкиите като стойности
        
  
  
    - В реда на нещата е функция да приема функция
 
  
  
  
func foo(bar func(int, float64) float64) float64 {
    return bar(5, 3.2)
}
 
  
  
    - Няма нищо лошо в това и да връщаме функция
 
  
  
  
func createRandomGenerator() func() int {
    return func() int {
        return 4
    }
}
 
  
  
    - Въобще, можем да ги присвояваме на стойност
 
  
    - Но можем да ги сравняваме само с 
nil 
  
    - Дори може да върне себе си
 
  
  
      
      
  
  
  
      
      
        ...или да изпълни себе си
        
  
package main
import "fmt"
func main() {
	fmt.Println("factorial(5) returns:")
	fmt.Println(factorial(5))
}
func factorial(x uint) uint {
    if x == 0 {
        return 1
    }
    return x * factorial(x-1)
}
 
      
      
  
  
  
      
      
        Именоване на функции
        
  
  
    - Кратки, описателни имена
 
  
    - Започва с буква, последвана от букви, цифри или _
 
  
    - Помните ли, че тук всеки стринг е UTF-8?
 
  
    ЗнаетеЛиЧеHelloWorldНаКитайскиЕ世界 е валидно име на функция 
  
    - НЕ пишете такива имена!
 
  
    - camelCase
 
  
    - Ако функцията ни трябва извън текущия пакет: CamelCase
 
  
  
      
      
  
  
  
      
      
        Анонимни функции
      
      
  
  
  
      
      
        func(x, y int) int { return x + y }
        
  
  
    - НЕ можем да дефинираме нормална функция в тялото на друга.
 
  
    - Но пък можем да създаваме ламбди
 
  
    - Ламбдите си нямат име... очевидно
 
  
    - Удобни са за дребни неща
 
  
  
  
package main
import "fmt"
func main() {
    add := func(x, y int) int { return x + y }
    fmt.Println(add(4, 5))
    fmt.Println(func(x, y int) int { return x + y }(3, 8))
}
 
      
      
  
  
  
      
      
        Методи
        
  
  
    За тях ще си говорим като стигнем до дефиниране на типове
  
  
      
      
  
  
  
      
      
        Указатели
      
      
  
  
  
      
      
        Указатели
        
  
  
    - Особен момент е, че нямаме аритметиката с указатели
 
  
    - Ако знаете как да ги ползвате в C/C++, нямате ядове
 
  
  
      
      
  
  
  
      
      
        За останалите: Опреснителен курс
        
  
  
    - Всички променливи, константи и функции се пазят в оперативната памет
 
  
    - Всяка запазена стойност стои в отделна клетка
 
  
    - Всяка клетка си има уникален адрес (0xb6802, 0xfx04001d7f0)
 
  
    - За да вземем адреса на дадена променлива, използваме унарния оператор 
& 
  
    - Имаме тип указател: 
*int 
  
    - Указателите са с константна големина
 
  
    - Указател може да сочи към указател
 
  
    - В Go една стойност се изчиства от паметта когато няма указатели към нея
 
  
    - Не можем да имаме указатели към константи
 
  
    - Очевидно 
intP == *(&intP) 
  
  
      
      
  
  
  
      
      
        Пример
        
  
package main
import "fmt"
var (
    name   string = "Чочко"
    age    uint8  = 27
    pName *string
)
func main() {
    pName = &name
    fmt.Printf("name е на адрес %p и има стойност %s\n", pName, name)
    fmt.Printf("age е на адрес %p и има стойност %d\n", &age, age)
}
 
      
      
  
  
  
      
      
        Как да си направим дупка в крака?
        
  
package main
func main() {
    var p *int = nil
    *p = 0
}
 
      
      
  
  
  
      
      
        Указатели и функции
        
  
  func fоо(a int, b *string) float64
 
  
  
  
    Функцията foo приема int и указател към string и връща float64
  
  
  
  
    Демек a бива копиран в скоупа на foo, а b просто сочи към някаква стойност отвън.
  
  
  
  
    b не се копира. Ако в него има около 652183859 символа, това е предимство 
  
    - Каквото и да правим с 
a не влияе на нищо извън тази функция 
  
    - Каквото и да направим с 
b променяме оригиналната стойност 
  
  
      
      
  
  
  
      
      
        Домашно