From 214aca6a6f7333862905c9dfd27c6445f550fe7b Mon Sep 17 00:00:00 2001 From: Slava Zgordan Date: Tue, 11 Aug 2015 11:01:57 +0200 Subject: [PATCH] 02.2 --- ru/02.2.md | 96 +++++++++++++++++++++++++----------------------------- 1 file changed, 45 insertions(+), 51 deletions(-) diff --git a/ru/02.2.md b/ru/02.2.md index 7ffd9bf1..a368db16 100644 --- a/ru/02.2.md +++ b/ru/02.2.md @@ -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