shad-go/lectures/01-basics/lecture.slide

735 lines
14 KiB
Text
Raw Normal View History

2020-02-20 14:40:51 +00:00
Базовые конструкции языка
Лекция 2
Фёдор Короткий
* Имена
25 ключевых слов.
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
* Predeclated identifiers
Константы
true false iota nil
Типы
any comparable int int8 int16 int32 int64
2020-02-20 14:40:51 +00:00
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
Функции
min max make len cap new append clear copy close delete
2020-02-20 14:40:51 +00:00
complex real imag
panic recover
Идентификаторы можно переопределять по обычным правилам
// just example, don't do this
var true = false
* Exported identifiers
Регистр первой буквы определяет видимость имени за пределами пакета
.play exported/example.go
Пакеты всегда называют в нижнем регистре и без подчеркиваний
fmt
grpcmiddleware
* Declaration
- 4 типы объявлений `var`, `const`, `type`, `func`
- Пакет состоит из множества `.go` файлов
- Порядок объявлений в коде не важен
* var
Общая форма
var name type = expression
Примеры
var i, j, k int // int, int, int
var b, f, s = true, 2.3, "four" // bool, float64, string
var f, err = os.Open(name) // os.Open returns a file and an error
* short variable declaration
i, j := 0, 1
Существующим переменным присваиваются новые значения
in, err := os.Open(infile)
// ...
out, err := os.Create(outfile)
Но должна объявляться хотябы одна новая переменная
f, err := os.Open(infile)
// ...
f, err := os.Create(outfile) // compile error: no new variables
* pointers
x := 1
p := &x // p, of type *int, points to x
fmt.Println(*p) // "1"
*p = 2
fmt.Println(x)
* escape analysis
var p = f()
func f() *int {
v := 1
return &v
}
* flag
.play flag/main.go
* new
`new(T)` создаёт новую переменную с типом `*T`.
2020-02-20 14:40:51 +00:00
func newInt() *int {
return new(int)
}
func newInt() *int {
var dummy int
return &dummy
}
* zero size type
p := new(int)
q := new(int)
fmt.Println(p == q) // "false"
p := new(struct{})
q := new(struct{})
fmt.Println(p == q) // "true" or "false", depending on implementation
a := [1_000_000_000]struct{}{}
fmt.Println(unsafe.Sizeof(a)) // 0
2020-02-20 14:40:51 +00:00
* variable lifetime
Память освобождается, после того как переменая становится недостижимой.
Компилятор может переместить переменную со стека на кучу.
var global *int
func f() {
var x int
x = 1
global = &x
}
И с кучи на стек.
func g() {
y := new(int)
*y = 1
}
.link https://habr.com/ru/companies/oleg-bunin/articles/676332/ Алгоритм для определения стек или куча для переменной
2020-02-20 14:40:51 +00:00
* type declaration
type name underlying-type
.play tempconv0/conv.go
- type alias
type name = another-type
2020-02-20 14:40:51 +00:00
* packages
- Файл `tempconv/types.go`
.play tempconv/tempconv.go
- Файл `tempconv/conv.go`
.play tempconv/conv.go
* packages
fmt.Printf("Brrrr! %v\n", tempconv.AbsoluteZeroC)
fmt.Println(tempconv.CToF(tempconv.BoilingC))
* package initialization
var (
a = b + c // a initialized third, to 3
b = f() // b initialized second, to 2, by calling f
c = 1 // c initialized first, to 1
)
2020-02-20 14:40:51 +00:00
func f() int { return c + 1 }
* package initialization
.play popcount/popcount.go
* scope
.play scope/scope.go
* scope
.play scope/if.go
* scope if
- Неправильно
if f, err := os.Open(fname); err != nil { // compile error: unused: f
return err
}
f.ReadByte() // compile error: undefined f
f.Close() // compile error: undefined f
- Правильно
f, err := os.Open(fname)
if err != nil {
return err
}
f.ReadByte()
f.Close()
* scope
var cwd string
func init() {
cwd, err := os.Getwd() // NOTE: wrong!
if err != nil {
log.Fatalf("os.Getwd failed: %v", err)
}
log.Printf("Working directory = %s", cwd)
}
* strings
`string` - неизменяемая последовательность байт.
`s[i]` - обращается к i-тому байту (не символу).
var s = "hello"
var doc = `Go is a tool for managing Go source code.
Usage:
go command [arguments]
...
`
* unicode
Символы кодируются числами.
type rune = int32
2020-02-20 14:40:51 +00:00
Кодировка utf8
2022-03-16 21:04:38 +00:00
0xxxxxxx runes 0127
110xxxxx 10xxxxxx 1282047
1110xxxx 10xxxxxx 10xxxxxx 204865535
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 655360x10ffff
2020-02-20 14:40:51 +00:00
Разница между рунами и байтами
import "unicode/utf8"
func countRunes() {
s := "Hello, 世界"
fmt.Println(len(s)) // "13"
fmt.Println(utf8.RuneCountInString(s)) // "9"
}
* utf8
for i := 0; i < len(s); {
r, size := utf8.DecodeRuneInString(s[i:])
fmt.Printf("%d\t%c\n", i, r)
i += size
}
Декодирование utf8 встроено в язык
for i, r := range "Hello, 世界" {
fmt.Printf("%d\t%q\t%d\n", i, r, r)
}
runes := []rune("Hello, 世界")
s := string(runes)
Некорректный байт превращается _unicode_replacement_character_ `'\uFFFD'`.
- Может ли строка `string([]rune(s))` быть больше `s`?
* stdlib
- `strings` - HasSuffix, Split, Join, etc.
- `bytes` - аналог `strings` для `[]byte`.
- `unicode` - IsDigit, IsLetter.
- `strconv` - конвертация между строкой и `int`, `float`.
- `path` - работа с unix путями
- `filepath` - работа с путями текущей платформы
* []byte
s := "abc"
b := []byte(s)
s2 := string(b)
* bytes.Buffer
func intsToBytes(values []int) []byte {
2020-02-20 14:40:51 +00:00
var buf bytes.Buffer
buf.WriteByte('[')
for i, v := range values {
if i > 0 {
buf.WriteString(", ")
}
fmt.Fprintf(&buf, "%d", v)
}
buf.WriteByte(']')
return buf.Bytes()
2020-02-20 14:40:51 +00:00
}
* constants
const (
a = 1
b
c = 2
d
)
fmt.Println(a, b, c, d) // "1 1 2 2"
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
)
type Flags uint
const (
FlagUp Flags = 1 << iota // is up
FlagBroadcast // supports broadcast access capability
FlagLoopback // is a loopback interface
FlagPointToPoint // belongs to a point-to-point link
FlagMulticast // supports multicast access capability
)
* untyped constants
const (
_ = 1 << (10 * iota)
KiB
MiB
GiB
TiB // (exceeds 1 << 32)
PiB
EiB
ZiB // (exceeds 1 << 64)
YiB
)
- Компилятор гарантирует как минимум 256 бит точности
fmt.Println(YiB/ZiB) // "1024"
* arrays
var a [3]int
var q [3]int = [3]int{1, 2, 3}
var r [3]int = [3]int{1, 2}
d := [...]int{1, 2, 3}
h := [...]int{99: -1}
- arrays are values
func zero(ptr *[32]byte) {
*ptr = [32]byte{}
}
* slices
- slice это тройка `data`, `len`, `cap`.
s := make([]int, 10)
s = s[:0]
s = s[:10]
s = s[:5:5]
s = s[:10] // panic
- make эквивалентен следующему коду
a := [10]int{}
s := a[:]
2020-02-20 14:40:51 +00:00
* slices
// reverse reverses a slice of ints in place.
func reverse(s []int) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
* slices
var a, b []string
// a == nil && b == nil
fmt.Println(a == b) // invalid operation: a == b (slice can only be compared to nil)
2020-02-20 14:40:51 +00:00
var c []int
d := []int{}
// c == nil && d != nil
// len(c) == 0 && len(d) == 0
* slices
make([]T, len) // len == cap
make([]T, len, cap)
func append(s []T, elem ...T) []T
var s []int
s = append(s, 1)
s = append(s, 2, 3)
var a, b []int
a = append(a, b...)
sCopy := append([]int(nil), s...) // use slices.Clone instead
2020-02-20 14:40:51 +00:00
* stack
stack = append(stack, v) // push v
top := stack[len(stack)-1] // top of stack
stack = stack[:len(stack)-1] // pop
* queue
queue = append(queue, v) // push v
front := queue[0] // front of queue
queue = queue[1:] // pop
- O(1) амортизированное время всех операций
- Не очень эффективно по числу аллокаций
2020-02-20 14:40:51 +00:00
* copy
func remove(slice []int, i int) []int {
copy(slice[i:], slice[i+1:])
return slice[:len(slice)-1]
}
* maps
ages := make(map[string]int)
ages := map[string]int{
"alice": 31,
"charlie": 34,
}
ages := make(map[string]int)
ages["alice"] = 31
ages["charlie"] = 34
* maps
ages["alice"] = 32
fmt.Println(ages["alice"]) // "32"
fmt.Println(ages["bob"]) // "0"
delete(ages, "alice")
ages["bob"] += 1
ages["bob"]++
_ = &ages["bob"] // compile error: cannot take address of map element
clear(ages)
2020-02-20 14:40:51 +00:00
* maps
for name, age := range ages {
fmt.Printf("%s\t%d\n", name, age)
}
var ages map[string]int
fmt.Println(ages == nil) // "true"
fmt.Println(len(ages) == 0) // "true"
fmt.Println(ages["alice"]) // "0"
ages["alice"] = 21 // panic
* maps
age, ok := ages["bob"]
if !ok { /* "bob" is not a key in this map; age == 0. */ }
if age, ok := ages["bob"]; !ok { /* ... */ }
* set
var s0 map[string]bool
var s1 map[string]struct{}
* struct
type Employee struct {
ID int
Name string
Address string
2020-02-20 19:15:38 +00:00
Salary int
2020-02-20 14:40:51 +00:00
}
var dilbert Employee
dilbert.Salary -= 5000 // demoted, for writing too few lines of code
* struct
type tree struct {
value int
left, right *tree
}
* struct
type Point struct{ X, Y int }
p := Point{1, 2}
p := Point{X: 1, Y: 2}
* comparing structs
type Point struct{ X, Y int }
p := Point{1, 2}
q := Point{2, 1}
fmt.Println(p.X == q.X && p.Y == q.Y) // "false"
fmt.Println(p == q) // "false"
Можно использовать структуры как ключи
type address struct {
hostname string
port int
}
hits := make(map[address]int)
hits[address{"golang.org", 443}]++
* struct embedding
type Point struct {
X, Y int
}
type Circle struct {
Point
Radius int
}
c := Circle{
Point: Point{X: 10, Y: 10},
Radius: 1,
}
c.X = 0
* json
type Movie struct {
Title string
Year int `json:"year"`
Color bool `json:"color,omitempty"`
Actors []string
}
- marshal
data, err := json.Marshal(movies)
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
data, err := json.MarshalIndent(movies, "", " ")
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
* unmarshal
var movie Movie
if err := json.Unmarshal(data, &movie); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
fmt.Println(movie)
* github
.play github/github.go /func Search/,/^}/
* functions
func name(parameter-list) (result-list) {
body
}
Примеры
func hypot(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(3, 4)) // "5"
func f(i, j, k int, s, t string) { /* ... */ }
func f(i int, j int, k int, s string, t string) { /* ... */ }
* multiple return values
func Get(url string) (*http.Response, error) {
2020-02-20 14:40:51 +00:00
// ...
if err != nil {
return nil, err
}
// ...
}
func logAndGet(url string) (*http.Response, error) {
2020-02-20 14:40:51 +00:00
log.Printf("logAndGet %s", url)
return Get(url)
}
* named return values
.play countwords/main.go /func Count/,/^}/
* errors
resp, err := http.Get(url)
if err != nil {
return nil, err
}
Дополнительный контекст
doc, err := html.Parse(resp.Body)
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %w", url, err)
}
Текст ошибки должен быть в lowercase.
genesis: crashed: no parachute: G-switch failed: bad relay orientation
* EOF
package io
import "errors"
// EOF is the error returned by Read when no more input is available.
var EOF = errors.New("EOF")
in := bufio.NewReader(os.Stdin)
for {
r, _, err := in.ReadRune()
if err == io.EOF {
break // finished reading
}
if err != nil {
return fmt.Errorf("read failed: %v", err)
}
// ...use r...
}
* variadic functions
func sum(vals ...int) int {
total := 0
for _, val := range vals {
total += val
}
return total
}
fmt.Println(sum()) // "0"
fmt.Println(sum(3)) // "3"
fmt.Println(sum(1, 2, 3, 4)) // "10"
values := []int{1, 2, 3, 4}
fmt.Println(sum(values...))
fmt.Println(sum(0, values...)) // compilation error
* function values
func Inc(i int) int { return i + 1 }
var f func(i int) int
if f != nil {
f = Inc
}
f = func(i int) int {
return i * 2
}
* recursion
type Node struct {
V int
L, R *Node
}
func PrintAll(w io.Writer, root *Node) {
var visit func(n *Node)
visit = func(n *Node) {
fmt.Fprintln(w, n.V)
if n.L != nil {
visit(n.L)
}
if n.R != nil {
visit(n.R)
}
}
2020-02-20 19:15:38 +00:00
visit(root)
2020-02-20 14:40:51 +00:00
}