Функции и указатели
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
променяме оригиналната стойност
Домашно