В этом уроке мы узнаем о массивах и срезах в Go.
Массивы
Что же такое массив?
Массив — это коллекция элементов фиксированного размера одного типа. Элементы массива хранятся последовательно, и доступ к ним можно получить по их index
.
Объявление
Мы можем объявить массив следующим образом:
var a [n]T
Здесь n
— это длина, а T
может быть любым типом, например, целым числом, строкой или пользовательской структурой.
Теперь объявим массив целых чисел длиной 4 и выведем его на печать.
func main() {
var arr [4]int
fmt.Println(arr)
}
$ go run main.go
[0 0 0 0]
По умолчанию все элементы массива инициализируются нулевым значением соответствующего типа массива.
Инициализация
Мы также можем инициализировать массив с помощью литерала массива.
var a [n]T = [n]T{V1, V2, ... Vn}
func main() {
var arr = [4]int{1, 2, 3, 4}
fmt.Println(arr)
}
$ go run main.go
[1 2 3 4]
Мы можем даже сделать сокращенное объявление.
...
arr := [4]int{1, 2, 3, 4}
Доступ к
Как и в других языках, мы можем получить доступ к элементам, используя index
, поскольку они хранятся последовательно.
func main() {
arr := [4]int{1, 2, 3, 4}
fmt.Println(arr[0])
}
$ go run main.go
1
Итерация
Теперь поговорим об итерации.
Итак, существует несколько способов итерации массивов.
Первый — это использование цикла for с функцией len
, которая выдает нам длину массива.
func main() {
arr := [4]int{1, 2, 3, 4}
for i := 0; i < len(arr); i++ {
fmt.Printf("Index: %d, Element: %dn", i, arr[i])
}
}
$ go run main.go
Index: 0, Element: 1
Index: 1, Element: 2
Index: 2, Element: 3
Index: 3, Element: 4
Другой способ — использовать ключевое слово range
с циклом for.
func main() {
arr := [4]int{1, 2, 3, 4}
for i, e := range arr {
fmt.Printf("Index: %d, Element: %dn", i, e)
}
}
$ go run main.go
Index: 0, Element: 1
Index: 1, Element: 2
Index: 2, Element: 3
Index: 3, Element: 4
Как мы видим, наш пример работает так же, как и раньше.
Но ключевое слово range является довольно универсальным и может быть использовано несколькими способами.
for i, e := range arr {} // Normal usage of range
for _, e := range arr {} // Omit index with _ and use element
for i := range arr {} // Use index only
for range arr {} // Simply loop over the array
Многомерность
Все массивы, которые мы создали до сих пор, являются одномерными. Мы также можем создавать многомерные массивы в Go.
Давайте рассмотрим пример:
func main() {
arr := [2][4]int{
{1, 2, 3, 4},
{5, 6, 7, 8},
}
for i, e := range arr {
fmt.Printf("Index: %d, Element: %dn", i, e)
}
}
$ go run main.go
Index: 0, Element: [1 2 3 4]
Index: 1, Element: [5 6 7 8]
Мы также можем позволить компилятору вывести длину массива, используя вместо длины ...
многоточие.
func main() {
arr := [...][4]int{
{1, 2, 3, 4},
{5, 6, 7, 8},
}
for i, e := range arr {
fmt.Printf("Index: %d, Element: %dn", i, e)
}
}
$ go run main.go
Index: 0, Element: [1 2 3 4]
Index: 1, Element: [5 6 7 8]
Свойства
Теперь поговорим о некоторых свойствах массивов.
Длина массива является частью его типа. Так, массивы a
и b
являются совершенно разными типами, и мы не можем присвоить один другому.
Это также означает, что мы не можем изменить размер массива, поскольку изменение размера массива означает изменение его типа.
package main
func main() {
var a = [4]int{1, 2, 3, 4}
var b [2]int = a // Error, cannot use a (type [4]int) as type [2]int in assignment
}
Массивы в Go являются типами значений, в отличие от других языков, таких как C, C++ и Java, где массивы являются ссылочными типами.
Это означает, что когда мы присваиваем массив новой переменной или передаем массив в функцию, копируется весь массив.
Таким образом, если мы вносим какие-либо изменения в этот скопированный массив, исходный массив не будет затронут и останется неизменным.
package main
import "fmt"
func main() {
var a = [7]string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
var b = a // Copy of a is assigned to b
b[0] = "Monday"
fmt.Println(a) // Output: [Mon Tue Wed Thu Fri Sat Sun]
fmt.Println(b) // Output: [Monday Tue Wed Thu Fri Sat Sun]
}
Слайсы
Я знаю, о чем вы думаете: массивы полезны, но немного негибки из-за ограничений, вызванных их фиксированным размером.
Это приводит нас к Slice, так что же такое срез?
Слайс — это сегмент массива. Слайсы основываются на массивах и обеспечивают большую мощность, гибкость и удобство.
Слайс состоит из трех частей:
- Указатель на базовый массив.
- Длина сегмента массива, который содержит фрагмент.
- И емкости, которая представляет собой максимальный размер, до которого может вырасти сегмент.
Подобно функции len
, мы можем определить емкость фрагмента с помощью встроенной функции cap
. Вот пример:
package main
import "fmt"
func main() {
a := [5]int{20, 15, 5, 30, 25}
s := a[1:4]
// Output: Array: [20 15 5 30 25], Length: 5, Capacity: 5
fmt.Printf("Array: %v, Length: %d, Capacity: %dn", a, len(a), cap(a))
// Output: Slice [15 5 30], Length: 3, Capacity: 4
fmt.Printf("Slice: %v, Length: %d, Capacity: %d", s, len(s), cap(s))
}
Не волнуйтесь, мы подробно обсудим все, что здесь показано.
Объявление
Давайте посмотрим, как мы можем объявить фрагмент.
var s []T
Как мы видим, нам не нужно указывать длину. Давайте объявим фрагмент целых чисел и посмотрим, как он работает.
func main() {
var s []string
fmt.Println(s)
fmt.Println(s == nil)
}
$ go run main.go
[]
true
Итак, в отличие от массивов, нулевым значением среза является nil
.
Инициализация
Существует несколько способов инициализации нашего среза. Один из способов — использовать встроенную функцию make
.
make([]T, len, cap) []T
func main() {
var s = make([]string, 0, 0)
fmt.Println(s)
}
$ go run main.go
[]
Подобно массивам, мы можем использовать литерал slice для инициализации нашего среза.
func main() {
var s = []string{"Go", "TypeScript"}
fmt.Println(s)
}
$ go run main.go
[Go TypeScript]
Другой способ — создать фрагмент из массива. Поскольку срез — это сегмент массива, мы можем создать срез от индекса low
до high
следующим образом.
a[low:high]
func main() {
var a = [4]string{
"C++",
"Go",
"Java",
"TypeScript",
}
s1 := a[0:2] // Select from 0 to 2
s2 := a[:3] // Select first 3
s3 := a[2:] // Select last 2
fmt.Println("Array:", a)
fmt.Println("Slice 1:", s1)
fmt.Println("Slice 2:", s2)
fmt.Println("Slice 3:", s3)
}
$ go run main.go
Array: [C++ Go Java TypeScript]
Slice 1: [C++ Go]
Slice 2: [C++ Go Java]
Slice 3: [Java TypeScript]
Отсутствие младшего индекса означает 0, а отсутствие старшего индекса означает len(a)
.
Здесь следует отметить, что мы можем создавать срезы из других срезов, а не только из массивов.
var a = []string{
"C++",
"Go",
"Java",
"TypeScript",
}
Итерация
Мы можем выполнять итерацию по фрагменту так же, как и по массиву, используя цикл for с функцией len
или ключевым словом range
.
Функции
Итак, теперь поговорим о встроенных функциях для работы с фрагментами, предоставляемых в Go.
- copy
Функция copy()
копирует элементы из одного фрагмента в другой. Она принимает 2 фрагмента, конечный и исходный. Она также возвращает количество скопированных элементов.
func copy(dst, src []T) int
Давайте посмотрим, как мы можем ее использовать.
func main() {
s1 := []string{"a", "b", "c", "d"}
s2 := make([]string, len(s1))
e := copy(s2, s1)
fmt.Println("Src:", s1)
fmt.Println("Dst:", s2)
fmt.Println("Elements:", e)
}
$ go run main.go
Src: [a b c d]
Dst: [a b c d]
Elements: 4
Как и ожидалось, наши 4 элемента из исходного фрагмента были скопированы в целевой фрагмент.
- добавление
Теперь давайте рассмотрим, как мы можем добавить данные в наш фрагмент с помощью встроенной функции append
, которая добавляет новые элементы в конец заданного фрагмента.
Она принимает фрагмент и переменное количество аргументов. Затем она возвращает новый фрагмент, содержащий все элементы.
append(slice []T, elems ...T) []T
Давайте попробуем это на примере, добавив элементы к нашему фрагменту.
func main() {
s1 := []string{"a", "b", "c", "d"}
s2 := append(s1, "e", "f")
fmt.Println("s1:", s1)
fmt.Println("s2:", s2)
}
$ go run main.go
s1: [a b c d]
s2: [a b c d e f]
Как мы видим, новые элементы были добавлены, и был возвращен новый фрагмент.
Но если данный фрагмент не имеет достаточной емкости для новых элементов, то выделяется новый базовый массив с большей емкостью.
Все элементы из базового массива существующего среза копируются в этот новый массив, а затем добавляются новые элементы.
Свойства
Наконец, давайте обсудим некоторые свойства срезов.
В отличие от массивов, срезы являются ссылочными типами.
Это означает, что изменение элементов фрагмента приведет к изменению соответствующих элементов в массиве, на который ссылается фрагмент.
package main
import "fmt"
func main() {
a := [7]string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
s := a[0:2]
s[0] = "Sun"
fmt.Println(a) // Output: [Sun Tue Wed Thu Fri Sat Sun]
fmt.Println(s) // Output: [Sun Tue]
}
Ломтики можно использовать и с переменными типами.
package main
import "fmt"
func main() {
values := []int{1, 2, 3}
sum := add(values...)
fmt.Println(sum)
}
func add(values ...int) int {
sum := 0
for _, v := range values {
sum += v
}
return sum
}
Эта статья является частью моего открытого курса по Go, доступного на Github.
karanpratapsingh / go-course
Освойте основы и расширенные возможности языка программирования Go
Курс по Go
Привет, добро пожаловать на курс, и спасибо за изучение Go. Я надеюсь, что этот курс обеспечит вам отличный опыт обучения.
Этот курс также доступен на моем сайте, а также на Educative.io
Оглавление
-
Начало работы
- Что такое Go?
- Зачем изучать Go?
- Установка и настройка
-
Глава I
- Hello World
- Переменные и типы данных
- Форматирование строк
- Управление потоком данных
- Функции
- Модули
- Пакеты
- Рабочие пространства
- Полезные команды
- Сборка
-
Глава II
- Указатели
- Структуры
- Методы
- Массивы и фрагменты
- Карты
-
Глава III
- Интерфейсы
- Ошибки
- Паника и восстановление
- Тестирование
- Дженерики
-
Глава IV
- Параллелизм
- Гороутины
- Каналы
- Выбрать
- Пакет синхронизации
- Расширенные шаблоны параллелизма
- Контекст
-
Приложение
- Следующие шаги
- Ссылки
Что такое Go?
Go (также известный как Golang) — это язык программирования, разработанный в Google в 2007 году и открытый в 2009 году.
Основное внимание в нем уделяется простоте, надежности и эффективности. Он был разработан, чтобы объединить эффективность, скорость и безопасность статически типизированного и компилируемого языка с легкостью…