This commit is contained in:
Slava Zgordan
2015-08-11 11:01:57 +02:00
parent 99488a5a12
commit 214aca6a6f

View File

@@ -1,27 +1,27 @@
# 2.2 Фундамент Go
В этом разделе мы научим Вас определять константы, переменные, относящиеся к элементарным типам данных и некоторым приемам программирования на Go.
В этом разделе мы научим Вас тому, как определять константы, переменные, относящиеся к элементарным типам данных, а также некоторым приемам программирования на Go.
## Определение переменных
В Go существует множество способов определить переменную.
Основной способ определить переменную в Go - с помощью ключевого слова 'var'. Заметьте, что в Go тип переменной ставится `после` ее имени.
Основной способ определить переменную в Go - с помощью ключевого слова 'var'. Заметьте, что в Go тип переменной ставится `после` ее имени:
// Определяем переменную “variableName” и тип "type"
var variableName type
Определение множества переменных.
Определение множества переменных:
// Определяем три переменных типа "type"
var vname1, vname2, vname3 type
Определение переменной с присваиванием ей значения.
Определение переменной с присваиванием ей значения:
// Определяем переменную “variableName” типа "type" и задаем ей значение "value"
var variableName type = value
Определение множества переменных с присваиванием им значений.
Определение множества переменных с присваиванием им значений:
/*
Определям три переменные типа "type" и инициализируем их значения.
@@ -40,18 +40,18 @@
Да, я понимаю, что этого недостаточно. Исправить это мы можем так:
/*
Определяем три переменные, не используя ключевые слова "type" и "var" и задаем им начальные значения.
Определяем три переменные, не используя ключевые слова "type" и "var", и задаем им начальные значения.
vname1 равно v1, vname2 равно v2, vname3 равно v3
*/
vname1, vname2, vname3 := v1, v2, v3
Так уже гораздо лучше. Используйте `:=` для замены `var` и `type`, это называется коротким объявлением. Но есть одно ограничение: такую форму определения можно использовать только внутри функций. Если Вы попытаетесь использовтаь ее вне тела функции, Вы получите ошибку компиляции. Поэтому можно использовать `var` для определения глобальных переменных и короткие объявления в `var()`.
Так уже гораздо лучше. Используйте `:=` для замены `var` и `type`, это называется коротким объявлением. Но есть одно ограничение: такую форму определения можно использовать только внутри функций. Если Вы попытаетесь использовать ее вне тела функции, Вы получите ошибку компиляции. Поэтому можно использовать `var` для определения глобальных переменных и короткие объявления в `var()`.
`_` (blank) - это специальное имя переменной. Любое значение, присвоенное такой переменной, будет проигнорировано. Например, мы присваиваем `35` переменной `b` и пропускаем `34`.( ***Этот пример просто призван показать, как это работает. Здесь не видно, в чем его польза, но мы будем часто использовать эту возможность Go в работе со значениями, возвращаемыми функциями.*** )
`_` (blank) - это специальное имя переменной. Любое значение, присвоенное такой переменной, будет проигнорировано. Например, мы присваиваем `35` переменной `b` и пропускаем `34`( ***Этот пример просто призван показать, как это работает. Здесь не видно, в чем его польза, но мы будем часто использовать эту возможность Go в работе со значениями, возвращаемыми функциями.*** ):
_, b := 34, 35
Если Вы определили переменную и не использовали ее нигде в своей программе, компилятор покажем Вам ошибку компиляции. Попробуйте откомпилировать следующий код и посмотрите, что будет.
Если Вы определили переменную и не использовали ее нигде в своей программе, компилятор покажет Вам ошибку компиляции. Попробуйте откомпилировать следующий код и посмотрите, что будет:
package main
@@ -93,9 +93,9 @@
### Числовые типы
Целочисленные типы включают в себя как целочисленные типы со знаком, так и без знака. В Go есть `int` и `uint` одновременно, у них одинаковая длина, но в каждом конкретном случае он азависит от операционной системы. Используются 32-битные типы в 32-битных операционных системах и 64-битные в 64-битных ОС. В Go также есть типы, у которых особая длина. К ним относятся `rune`, `int8`, `int16`, `int32`, `int64`, `byte`, `uint8`, `uint16`, `uint32`, `uint64`. Заметьте, что `rune` - это алиас `int32`, а `byte` - это алиас `uint8`.
Целочисленные типы включают в себя как целочисленные типы со знаком, так и без знака. В Go есть и `int`, и `uint`, у них одинаковая длина, но в каждом конкретном случае она зависит от операционной системы. В 32-битных системах используются 32-битные типы, в 64-битных - 64-битные. В Go также есть типы, у которых особая длина. К ним относятся `rune`, `int8`, `int16`, `int32`, `int64`, `byte`, `uint8`, `uint16`, `uint32`, `uint64`. Заметьте, что `rune` - это алиас `int32`, а `byte` - это алиас `uint8`.
Есть одна важная вещь, которую надо знать - Вы не можете комюинировать разные типы в одном выражении, такая операция повлечет ошибку компиляции.
Есть одна важная вещь, которую надо знать - Вы не можете комбинировать разные типы в одном выражении, такая операция повлечет ошибку компиляции:
var a int8
@@ -103,12 +103,11 @@
c := a + b
Хотя int32 длиннее int8 и является тем же типом, что и int, нельзя использовать их в одним выражении. ( ***c здесь будет определено как переменная типа `int`*** )
Хотя int32 длиннее int8 и является тем же типом, что и int, нельзя использовать их в одним выражении. ( ***'c' здесь будет определена как переменная типа `int`*** )
К типам с плавающей точкой относятся `float32` и `float64`, типа, назвывемого `float` в Go нет. `float64` используется по умолчанию при коротком объявлении.
К типам с плавающей точкой относятся `float32` и `float64`; типа, называемого `float` в Go нет. `float64` используется по умолчанию при коротком объявлении.
That's all? No! Go supports complex numbers as well. `complex128` (with a 64-bit real and 64-bit imaginary part) is the default type, if you need a smaller type, there is one called `complex64` (with a 32-bit real and 32-bit imaginary part). Its form is `RE+IMi`, where `RE` is real part and `IM` is imaginary part, the last `i` is imaginary number. There is a example of complex number.
Это все? Нет! Go также поддерживает и комплексные числа. `complex128` (с 64-битной вещественной и 64-битной мнимыми частями) является комплексным числом по умолчанию, а если Вам нужны числа поменьше, есть `complex64` (с 32-битной вещественной и 32-битной нмимыми частями). Числа представлены в форме `RE+IMi`, где `RE` - вещественная часть, а `IM` - мнимая, последнее `i` - мнимая единица. Вот пример комплексной единицы:
Это все? Нет! Go также поддерживает и комплексные числа. `complex128` (с 64-битной вещественной и 64-битной мнимыми частями) является комплексным числом по умолчанию, а если Вам нужны числа поменьше, есть `complex64` (с 32-битной вещественной и 32-битной нмимыми частями). Числа представлены в форме `RE+IMi`, где `RE` - вещественная часть, а `IM` - мнимая, последнее `i` - мнимая единица. Вот пример комплексного числа:
var c complex64 = 5+5i
//output: (5+5i)
@@ -116,7 +115,7 @@ That's all? No! Go supports complex numbers as well. `complex128` (with a 64-bit
### Строки
Мы уже говорили о том, как Go использует кодировку UTF-8. Строки представлены двойными кавычками `""` обратными кавычками ``` `` ```.
Мы уже говорили о том, как Go использует кодировку UTF-8. Строки представлены двойными кавычками `""` или обратными кавычками ``` `` ```.
// Пример кода
var frenchHello string // основная форма определения строки
@@ -153,13 +152,12 @@ That's all? No! Go supports complex numbers as well. `complex128` (with a 64-bit
s = "c" + s[1:] // нельзя менять строку по индексу, но получать значения по индексу можно
fmt.Printf("%s\n", s)
What if I want to have a multiple-line string?
Что, если мы захотим определить строку, чтобы она визуально находилась на разных строках?
Что, если мы захотим определить строковую переменную, значение которой располагается на разных строках?
m := `hello
world`
`` ` все символы в строке воспринимает буквально, как часть значения переменной.
``` все символы в строке воспринимает буквально, как часть значения переменной.
### Типы ошибок
@@ -182,8 +180,7 @@ What if I want to have a multiple-line string?
### Групповое определение
If you want to define multiple constants, variables or import packages, you can use the group form.
Если Вы хотите определить сразу несколько констант, переменных или импортировать несколько пакетов, Вы можете использовать групповое определение:
Если Вы хотите определить сразу несколько констант, переменных или импортировать несколько пакетов, Вы можете использовать групповое определение.
Основная форма:
@@ -217,7 +214,7 @@ If you want to define multiple constants, variables or import packages, you can
prefix string
)
Если не указать, что значение константы равно `iota`, то значение первой константы в группе `const()` будет равно `0`. Если же константе, перед которой есть другая константа, явно не присвоено никакого значения, оно будут равно значению идущей перед ней константы. Если значение константы указано как `iota`, значения последующих после нее констант в группе, которым явно не присвоено никаких значений, также будут равны `iota` (И так до тех пор, пока не встретится константа с явно указанным значением, после этого значения всех идущих после нее констант с явно неуказанным значением будут равны ее значению - прим. переводчика на русский)
Если не указать, что значение константы равно `iota`, то значение первой константы в группе `const()` будет равно `0`. Если же константе, перед которой есть другая константа, явно не присвоено никакого значения, оно будет равно значению идущей перед ней константы. Если значение константы указано как `iota`, значения последующих после нее констант в группе, которым явно не присвоено никаких значений, также будут равны `iota` (И так до тех пор, пока не встретится константа с явно указанным значением, после этого значения всех идущих после нее констант с явно неуказанным значением будут равны ее значению - прим. переводчика на русский).
### Перечисление iota
@@ -230,7 +227,7 @@ If you want to define multiple constants, variables or import packages, you can
w // если значение константы не указано, ей присваивается значение идущей перед ней константы, следовательно здесь также получается w = iota. Поэтому w == 3, а в случаях y и x также можно было бы опустить "= iota".
)
const v = iota // так как iota встречает ключевое слово `const`, происходит сброс на `0`, поэтому v = 0.
const v = iota // так как iota встречает ключевое слово `const`, происходит сброс на 0, поэтому v = 0.
const (
e, f, g = iota, iota, iota // e=0,f=0,g=0, значения iota одинаковы, так как находятся на одной строке.
@@ -243,19 +240,19 @@ If you want to define multiple constants, variables or import packages, you can
- Все переменные, имя которых начинается с большой буквы, являются публичными; те, имя которых начинается с маленькой буквы - приватными.
- То же относится к функциям и константам, в Go нет ключевых слов `public` или `private`.
## array, slice, map
## Массивы, срезы, карты
### array
### Массив
`array` - это массив, он определяется так:
Массив в Go определяется так:
var arr [n]тип
В `[n]тип`, `n` - длина массива, `type` тип его элементов. Так же, как и в других языках, квадратные скобки `[]`используются для того, чтобы получить или присвоить значения элементам массива.
В `[n]тип`, `n` - длина массива, `type` - тип его элементов. Так же, как и в других языках, квадратные скобки `[]`используются для того, чтобы получить или присвоить значения элементам массива.
var arr [10]int // массив типа [10]int
arr[0] = 42 // первый элемент массива имеет индекс 0
arr[1] = 13 // присвуаивание значение элементу массива
arr[1] = 13 // присваивание значения элементу массива
fmt.Printf("Первый элемент - %d\n", arr[0]) // получаем значение элемента массива, оно равно 42
fmt.Printf("Последний элемент - %d\n", arr[9]) // возвращается значение по умолчанию 10-го элемента этого массива, в данном случае оно равно 0.
@@ -267,7 +264,7 @@ If you want to define multiple constants, variables or import packages, you can
b := [10]int{1, 2, 3} // определяем целочисленный массив длиной 10 элементов, из которых первым трем присваиваем значения. Остальным элементам присваивается значение по умолчанию 0.
c := [...]int{4, 5, 6} // используйте `…` вместо значения длины, и Go посчитает его за Вас.
c := [...]int{4, 5, 6} // используйте `` вместо значения длины, и Go посчитает ее за Вас.
Вам может захотеться использовать массивы в качестве элементов другого массива. Давайте посмотрим, как это делается:
@@ -277,18 +274,17 @@ If you want to define multiple constants, variables or import packages, you can
// То же самое более коротким способом:
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
Структура, лежащая в основе массива.
Структура, лежащая в основе массива:
![](images/2.2.array.png?raw=true)
Рисунок 2.2 Отношения внутри многомерного массива
### slice
### Срезы
Часто тип array - не очень подходящий тип, например, когда при определении мы не знаем точно, какова будет длина массива. Поэтому нам нужен "динамический массив". В Go такой динамический массив называется `срезом (slice)`.
Часто тип 'массив' - не очень подходящий тип, например, когда при определении мы не знаем точно, какова будет длина массива. Поэтому нам нужен "динамический массив". В Go такой динамический массив называется `срезом (slice)`.
`slice` is not really a `dynamic array`. It's a reference type. `slice` points to an underlying `array` whose declaration is similar to `array`, but doesn't need length.
Вообще `срез` - это не `динамический массив`. Это ссылочный тип. `срез` указывает на лежащий в его основе `массив`, его объявление аналогично `массиву`, но длина не указывается.
Вообще `срез` - это не `динамический массив`. Это ссылочный тип. `срез` указывает на лежащий в его основе `массив`, его объявление аналогично объявлению `массива`, но длина не указывается.
// так же, как мы объявляем массив, но здесь мы не указываем длину
var fslice []int
@@ -297,7 +293,7 @@ If you want to define multiple constants, variables or import packages, you can
slice := []byte {'a', 'b', 'c', 'd'}
`Срез` может переопределять существующие массивы и срезы. `slice` использует `array[i:j]`, чтобы получить фрагмент массива, где `i` - начальный индекс, а `j` - конечный, но имейте в виду, что `array[j]` не войдет в срез, так как длина среза равна `j-i`.
`Срез` может переопределять существующие массивы и срезы. `Срез` использует `array[i:j]`, чтобы получить фрагмент массива, где `i` - начальный индекс, а `j` - конечный, но имейте в виду, что `array[j]` не войдет в срез, так как длина среза равна `j-i`.
// Определяем массив длиной 10 элементов, элементы являются значениями типа byte.
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
@@ -315,7 +311,7 @@ If you want to define multiple constants, variables or import packages, you can
Имейте в виду разницу между `срезом` и `массивом`, когда определяете их. Для вычисления длины массива используется `[…]`, но для определения среза - только `[]`.
Структура, лежащая в основе срезов.
Структура, лежащая в основе срезов:
![](images/2.2.slice.png?raw=true)
@@ -323,7 +319,7 @@ If you want to define multiple constants, variables or import packages, you can
Со срезами можно производить некоторые удобные операции:
- Первый элемент среза имеет индекс 0, `ar[:n]` равен `ar[0:n]`
- Первый элемент среза имеет индекс 0, `ar[:n]` равен `ar[0:n]`.
- Если второй индекс элемента не указан, он равен длине среза, `ar[n:]` равен `ar[n:len(ar)]`.
- Можно использовать `ar[:]`, чтобы срез был равен всему массиву, как следует из сказанного в двух предыдущих пунктах.
@@ -346,9 +342,9 @@ If you want to define multiple constants, variables or import packages, you can
bSlice = aSlice[0:5] // срез может быть расширен до значения емкости, теперь bSlice содержит d,e,f,g,h
bSlice = aSlice[:] // bSlice содержит все элементы aSlice или d,e,f,g
`slice` - ссылочный тип, поэтому при его изменении изменятся также значения всех остальных переменных, указывающих на тот же срез или массив. Например, возвращаясь к `aSlice` и `bSlice`, о которых шла речь выше, если изменить значение одного из элементов `aSlice`, `bSlice` тоже будет изменен.
`Срез` - ссылочный тип, поэтому при его изменении изменятся также значения всех остальных переменных, указывающих на тот же срез или массив. Например, возвращаясь к `aSlice` и `bSlice`, о которых шла речь выше, если изменить значение одного из элементов `aSlice`, `bSlice` тоже будет изменен.
`slice` похож на struct и состоит из 3 частей:
`Срез` похож на struct и состоит из 3 частей:
- Указатель на то, где начинается `срез`.
- Длина `среза`.
@@ -370,13 +366,13 @@ If you want to define multiple constants, variables or import packages, you can
- `append` присоединяет к `срезу` один или несколько элементов и возвращает новый `срез` .
- `copy` копирует элементы из одного среза в другой и возвращает количество скопированных элементов.
Внимание: `append` изменяет массив, на который указывает `срез` и затрагивает все остальные срезы, указывающие на тот же массив. Также, если срезу не хватает длины массива (`(cap-len) == 0`), `append` возвращает новый массив, на который теперь будет указывать этот срез. В этом случае другие срезы, указывающие на старый массив, не изменятся.
Внимание: `append` изменяет массив, на который указывает `срез` и затрагивает все остальные срезы, указывающие на тот же массив. Также, если срезу не хватает длины массива (`(cap-len) == 0`), `append` возвращает новый массив, на который теперь будет указывать этот срез. В этом случае значения других срезов, указывающих на старый массив, не изменятся.
### Карты
Поведение `карты (map)` похоже на то, как ведет себя словарь в Python. Чтобы определить карту, используйте `map[типКлюча]типЗначения`.
Давайте посмотрим на пример кода. Команды 'изменения' и 'получения' значений в `карте` похожи на соответствующие для `среза`, однако индекс для `среза` может быть только типа 'int', в то время как `карта` может использовать для этих целей гораздо больше: например `int`, `string` или вообще все что захотите. Также можно использовать `==` и `!=`, чтобы сравнивать значения между собой.
Давайте посмотрим на пример кода. Команды изменения и получения значений для `карты` похожи на соответствующие для `среза`, однако индекс для `среза` может быть только типа 'int', в то время как `карта` может использовать для этих целей гораздо больше: например `int`, `string` или вообще все, что захотите. Также можно использовать `==` и `!=`, чтобы сравнивать значения между собой.
// используем `string` для задания типа ключа, `int` для задания типа значения и инициализируем карту с помощью `make`.
var numbers map[string] int
@@ -396,12 +392,11 @@ If you want to define multiple constants, variables or import packages, you can
- `len (длина)` работает также и с `картой`. Она возвращает количество `ключей` в карте.
- Изменить значение в `карте` очень просто. Чтобы изменить значение `ключа` one на `11`, нужно использовать выражение `numbers["one"]=11`.
You can use form `key:val` to initialize map's values, and `map` has built-in methods to check if the `key` exists.
Чтобы задать значения элементам карты, нужно исполоьзовать форму `ключ:значение`, также у `карты` есть встроенные методы для того, чтобы проверить, содержит ли она заданный ключ.
Чтобы задать значения элементам карты, нужно использовать форму `ключ:значение`, также у `карты` есть встроенные методы для того, чтобы проверить, содержит ли она заданный ключ.
Для того, чтобы удалить элемент в `карте`, используйте `delete`.
// Initialize a map
// Задаем карте начальное значение
rating := map[string]float32 {"C":5, "Go":4.5, "Python":4.5, "C++":2 }
// карта возвращает два значения. В качестве второго, если элемента с таким ключом не существует, 'ok' возвращает 'false', иначе - 'true'.
csharpRating, ok := rating["C#"]
@@ -411,9 +406,9 @@ You can use form `key:val` to initialize map's values, and `map` has built-in me
fmt.Println("Не можем найти рейтинг C# в карте")
}
delete(rating, "C") // удаляем элемент с ключом "c"
delete(rating, "C") // удаляем элемент с ключом "C"
Как я уже говорил выше, `карта` является ссылочным типом. Если две `карты`s указываюьт на один и тот же объект, любое его изменение затронет их обе.
Как я уже говорил выше, `карта` является ссылочным типом. Если две `карты` указывают на один и тот же объект, любое его изменение затронет обе карты:
m := make(map[string]string)
m["Hello"] = "Bonjour"
@@ -422,23 +417,22 @@ You can use form `key:val` to initialize map's values, and `map` has built-in me
### make, new
`make` выделяет память для объектов встроенных типов, таких как `map`, `slice`, и `channel`, в то время как `new` служит для выделения памяти под сами типы.
`make` выделяет память для объектов встроенных типов, таких как `карта`, `срез`, и `канал`, в то время как `new` служит для выделения памяти под сами типы.
`new(T)` размещает в памяти нулевое значение типа `T` и возвращает его адрес в памяти, типом которого является `*T`. По определению Go оно возвращает указатель на нулевое значение типа `T`.
`new(T)` размещает в памяти нулевое значение типа `T` и возвращает его адрес в памяти, типом которого является `*T`. В терминах Go оно возвращает указатель на нулевое значение типа `T`.
`new` возвращает указатели.
У встроенной функции `make(T, args)` другое предназначение, нежели у `new(T)`. `make` используется для `slice(срезов)`, `map(карт)` и `channel(каналов)` и возвращает стартовое значение типа `T`. Это делается потому, что данные для этих трех типов должны быть изначально проинициализированы перед тем, как на них указывать. Например, `срез(slice)` содержит указатель, который указывает на лежащий в его основе `array(массив)`, его длину и емкость. Перед тем, как эти данные инициализированы, значение `slice` равно `nil`, поэтому для `slice`, `map` и `channel`, `make` нициализирует лежащие в их основе данные и присваивает некоторые подходящие значения.
У встроенной функции `make(T, args)` другое предназначение, нежели у `new(T)`. `make` используется для `slice(срезов)`, `map(карт)` и `channel(каналов)` и возвращает стартовое значение типа `T`. Это делается потому, что данные для этих трех типов должны быть изначально проинициализированы перед тем, как на них указывать. Например, `срез(slice)` содержит указатель, который указывает на лежащий в его основе `array(массив)`, его длину и емкость. Перед тем, как эти данные инициализированы, значение `slice` равно `nil`, поэтому для `slice`, `map` и `channel`, `make` инициализирует лежащие в их основе данные и присваивает некоторые подходящие значения.
`make` возвращает ненулевые значения.
Следующий рисунок показывает, как отличаются`new` и `make`.
Следующий рисунок показывает, как отличаются `new` и `make`.
![](images/2.2.makenew.png?raw=true)
Рисунок 2.5 Выделение памяти для данных, лежащих в основе в случае make и new
Zero-value does not mean empty value. It's the value that variables default to in most cases. Here is a list of some zero-values.
Нулевое значение - это не пустое значение. Это то значение, которое в большинстве случаев является значением по умолчанию для переменной соответствующего типа. Вот список нулевых значений для некоторых типов:
int 0