Массивы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
Многие программы работают со списками. Списки адресов. Списки телефонных номеров. Списки товаров. Массив представляет собой набор значений, относящихся к одному типу. Представьте себе таблетницу — вы можете класть и доставать таблетки в любое отделение, но при этом ее удобно переносить как единое целое. Значения, хранящиеся в массиве, называются элементами. Вы можете создать массив строк, массив логических значений или массив любого другого типа Go (даже массив массивов). Весь массив можно сохранить в одной переменной, а затем обратиться к любому нужному элементу. В массивах хранятся наборы значений. Массив содержит заранее заданное количество элементов, а его размер не может увеличиваться или уменьшаться. Чтобы объявить переменную для хранения массива, следует указать количество хранящихся в нем элементов в квадратных скобках ([]), а затем тип элементов в массиве. Чтобы присвоить значения элементам массива или прочитать их позднее, необходимо каким-то образом указать, какой элемент вам нужен. Элементы в массиве нумеруются, начиная с 0. Номер массива называется его индексом. Пример массив нот: var notes [7]string notes[0] = "do" notes[1] = "re" notes[2] = "mi" fmt.Println(notes[0]) fmt.Println(notes[1]) Массив целых чисел: var primes [5]int primes[0] = 2 primes[1] = 3 fmt.Println(primes[0]) Массив значений time.Time: var dates [3]time.Time dates[0] = time.Unix(1257894000, 0) dates[1] = time.Unix(1447920000, 0) dates[2] = time.Unix(1508632200, 0) fmt.Println(dates[1]) Нулевые значения в массивах Как и в случае с переменными, при создании массивов все содержащиеся в них значения инициализируются нулевым значением для типа, содержащегося в массиве. Так массив значений int по умолчанию заполняется нулями. С другой стороны, нулевым значением для строк является пустая строка, так что массив строковых значений по умолчанию заполняется пустыми строками. Нулевые значения позволяют безопасно выполнять операции с элементами массивов, даже если им не были присвоены значения. Например, в следующем массиве хранятся целочисленные счетчики. Любой элемент можно увеличить на 1 даже без предварительного присваивания значения, потому что мы знаем, что все значения счетчиков начинаются с 0. var counters [3]int counters[0]++ // Первый элемент увеличивается с 0 до 1. counters[0]++ // Первый элемент увеличивается с 1 до 2. counters[2]++ // Третий элемент увеличивается с 0 до 1. fmt.Println(counters[0], counters[1], counters[2]) Вывод: 2 0 1 При создании массива все содержащиеся в нем элементы инициализируются нулевым значением для типа, хранящегося в массиве. |
Литералы массивов
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
Если вам заранее известны значения, которые должны храниться в массиве, вы можете инициализировать массив этими значениями в форме литерала массива. Литерал массива начинается как тип массива — с количества элементов в квадратных скобках, за которым следует тип элементов. Далее в фигурных скобках идет список исходных значений элементов массива. Значения элементов должны разделяться запятыми. [3]int{9, 18, 27} | | | | | Список значений, разделенных запятыми. | Тип элементов в массиве. | Количество элементов в массиве. var notes [7]string = [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} fmt.Println(notes[3], notes[6], notes[0]) var primes [5]int = [5]int{2, 3, 5, 7, 11} fmt.Println(primes[0], primes[2], primes[4]) Литералы массивов также позволяют использовать короткие объявления переменных с помощью :=. Короткое объявление переменной. notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} primes := [5]int{2, 3, 5, 7, 11} Литералы массивов могут распространяться на несколько строк, но перед каждым переносом строки в коде должна стоять запятая. Запятая даже должна стоять после последнего элемента в литерале массива, если за ним следует перенос строки. (На первый взгляд этот синтаксис выглядит неуклюже, но он упрощает последующее добавление новых элементов в коде.) text := [3]string{ // Все это один массив. "This is a series of long strings", "which would be awkward to place", "together on a single line", // Запятая в конце обязательна. } Когда вы занимаетесь отладкой кода, вам не нужно передавать элементы массивов Println и другим функциям пакета fmt один за одним. Просто передайте весь массив. Пакет fmt содержит логику форматирования и вывода массивов. (Пакет fmt также умеет работать с сегментами, картами и другими структурами данных, которые будут описаны позднее.) var notes [3]string = [3]string{"do", "re", "mi"} var primes [5]int = [5]int{2, 3, 5, 7, 11} fmt.Println(notes) fmt.Println(primes) Возможно, вы также помните глагол "%#v", используемый функциями Printf и Sprintf, — он форматирует значения так, как они отображаются в коде Go. При форматировании с "%#v" массивы отображаются в форме литералов массивов Go. fmt.Printf("%#v\n", notes) fmt.Printf("%#v\n", primes) |
Обращение к элементам массива в цикле
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Вы не обязаны явно записывать целочисленные индексы элементов массивов, к которым обращаетесь в своем коде. В качестве индекса также можно использовать значение целочисленной переменной. notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} index := 1 fmt.Println(index, notes[index]) // Выводит элемент массива с индексом 1. index = 3 fmt.Println(index, notes[index]) // Выводит элемент массива с индексом 3. for i := 0; i <= 2; i++ { fmt.Println(i, notes[i]) } При обращении к элементам массивов через переменную необходимо действовать внимательно и следить за тем, какие значения индексов используются в программе. Как упоминалось ранее, массивы содержат конкретное число элементов. Попытка обратиться к индексу за пределами массива приводит к панике — ошибке, происходящей во время выполнения программы (а не на стадии компиляции). |
Проверка длины массива функцией «len»
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Написание циклов, которые ограничиваются только правильными индексами, сопряжено с определенным риском ошибок. К счастью, есть пара приемов, которые упрощают этот процесс. Во-первых, вы можете проверить фактическое количество элементов в массиве перед обращением к элементу. Для этого можно воспользоваться встроенной функцией len, которая возвращает длину массива (количество содержащихся в нем элементов). notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} for i := 0; i < len(notes); i++ { fmt.Println(i, notes[i]) } Впрочем, и здесь существует некоторый риск ошибок. Хотя len(notes) возвращает наибольший индекс, к которому вы можете обращаться, равен 6 (потому что индексирование массивов начинается с 0, а не с 1). При попытке обратиться по индексу 7 возникнет ситуация паники. |
Безопасный перебор массивов в цикле «for…range»
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
В другом, еще более безопасном способе обработки всех элементов массива используется специальный цикл for...range. В форме с range указывается переменная для хранения целочисленного индекса каждого элемента, другая переменная для хранения значения самого элемента и перебираемый массив. Цикл выполняется по одному разу для каждого элемента в массиве; индекс элемента присваивается первой переменной, а значение элемента — второй переменной. В блок цикла включается код для обработки этих значений. for index, value := range myArray { // Блок цикла. } Эта форма цикла for не содержит запутанных выражений инициализации, условия и завершения. А поскольку значение элемента автоматически присваивается переменной, риск обращения к недействительному индексу массива исключен. Форма цикла for с range читается безопаснее и проще, поэтому именно она чаще всего встречается при работе с массивами и другими коллекциями. notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} for index, note := range notes { fmt.Println(index, note) } Цикл выполняется семь раз, по одному разу для каждого элемента в массиве notes. Для каждого элемента переменной index присваивается индекс элемента, а переменной note присваивается значение элемента. После этого мы выводим индекс и значение. Помните, как при вызове функции с несколькими возвращаемыми значениями мы хотели проигнорировать одно из них? Это значение присваивалось пустому идентификатору ( _ ), чтобы компилятор Go просто отбросил это значение без выдачи сообщения об ошибке... То же самое можно проделать со значениями из циклов "for...range". Если вам не нужен индекс каждого элемента массива, присвойте его пустому идентификатору: notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} for index, note := range notes { fmt.Println(index, note) } А если вам не нужна переменная для значения, замените ее пустым идентификатором |