shad-go/lectures/01-basics/lecture.slide
Fedor Korotkiy c251f70f92 Fix slides
2020-02-20 22:15:38 +03:00

701 lines
13 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Базовые конструкции языка
Лекция 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
Типы
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
Функции
make len cap new append copy close delete
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`.
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
* variable lifetime
Память освобождается, после того как переменая становится недостижимой.
Компилятор может переместить переменную со стека на кучу.
var global *int
func f() {
var x int
x = 1
global = &x
}
И с кучи на стек.
func g() {
y := new(int)
*y = 1
}
* type declaration
type name underlying-type
.play tempconv0/conv.go
* 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
var b = f() // b initialized second, to 2, by calling f
var c = 1 // c initialized first, to 1
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
Кодировка utf8
0xxxxxx runes 0127
11xxxxx 10xxxxxx 1282047
110xxxx 10xxxxxx 10xxxxxx 204865535
1110xxx 10xxxxxx 10xxxxxx 10xxxxxx 655360x10ffff
Разница между рунами и байтами
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 intsToString(values []int) string {
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.String()
}
* 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]
* 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
fmt.Println(a == b)
var c []int
d := []int{}
fmt.Println(c != d)
// 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...)
* stack
stack = append(stack, v) // push v
top := stack[len(stack)-1] // top of stack
stack = stack[:len(stack)-1] // pop
* 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
* 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
Salary int
}
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.Responce, error) {
// ...
if err != nil {
return nil, err
}
// ...
}
func logAndGet(url string) (*http.Responce, error) {
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)
}
}
visit(root)
}