Урок 4 «Учитесь работать быстро» — Неброские массивы

задняя часть Go переводчик
Урок 4 «Учитесь работать быстро» — Неброские массивы

Массив представляет собой часть непрерывной памяти. Почти все компьютерные языки имеют массивы. Однако массивы в языке Go обычно не используются. Это связано с тем, что массивы являются статическими с фиксированной длиной. После того, как длина определена, длина не может быть массивы длин относятся к разным типам и не могут быть преобразованы и присвоены друг другу, что неудобно в использовании.

Срезы — это динамические массивы, которые можно расширять для увеличения их длины. Когда длина не меняется, он работает как обычный массив. Когда длины различны, они также принадлежат к одному типу и могут быть присвоены друг другу. Удобство нарезки привело к повсеместной замене массивов в большинстве областей применения.

Однако не стоит недооценивать массивы. В базовой реализации срезов массивы являются краеугольным камнем срезов. Специальный синтаксис срезов скрывает внутренние детали, поэтому пользователи не могут напрямую видеть скрытые внутри массивы. Срезы — не что иное, как оболочка вокруг массивов, дающая упрямым числам гибкие крылья, позволяющие камням летать.

Просто приведенное выше текстовое описание, читатели определенно будут очень сбиты с толку. Давайте рассмотрим конкретные примеры.

Определение переменных массива

Попробуем только объявить тип и не присваивать начальное значение. В этом случае компилятор по умолчанию присвоит массиву «нулевое значение». Нулевое значение массива — это нулевое значение всех внутренних элементов.

package main

import "fmt"

func main() {
	var a [9]int
	fmt.Println(a)
}

------------
[0 0 0 0 0 0 0 0 0]

Давайте взглянем на другие три формы определения переменных, эффект тот же.

package main

import "fmt"

func main() {
	var a = [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	var b [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	c := [8]int{1, 2, 3, 4, 5, 6, 7, 8}
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
}

---------------------
[1 2 3 4 5 6 7 8 9]
[1 2 3 4 5 6 7 8 9 10]
[1 2 3 4 5 6 7 8]

доступ к массиву

Затем мы используем индексы, чтобы просто манипулировать массивом, в котором хранится квадратное значение числа.

package main

import "fmt"

func main() {
	var squares [9]int
	for i := 0; i < len(squares); i++ {
		squares[i] = (i + 1) * (i + 1)
	}
	fmt.Println(squares)
}

--------------------
[1 4 9 16 25 36 49 64 81]

Проверка выхода за границы индекса массива (расширенные знания)

В приведенном выше коде мы заметили, что мы можем использовать встроенную функцию len() для прямого получения длины массива. Длина массива определяется во время компиляции, когда мы используем функцию len() для доступа к свойству длины массива, компилятор тайно заменяет его целочисленным значением за кулисами.

package main

import "fmt"

func main() {
	var a = [5]int{1,2,3,4,5}
	a[101] = 255
	fmt.Println(a)
}

-----
./main.go:7:3: invalid array index 101 (out of bounds for 5-element array)

Результат выполнения приведенного выше кода показывает, что язык Go будет выполнять проверку компилятором на наличие индексов доступа к массиву за пределами границ. Важный вопрос: если нижний индекс является переменной, как Go проверяет, что нижний индекс выходит за пределы? Переменные должны определяться во время выполнения, находятся ли они за пределами границ Как Go это делает?

package main

import "fmt"

func main() {
	var a = [5]int{1,2,3,4,5}
	var b = 101
	a[b] = 255
	fmt.Println(a)
}

------------
panic: runtime error: index out of range

goroutine 1 [running]:
main.main()
	/Users/qianwp/go/src/github.com/pyloque/practice/main.go:8 +0x3d
exit status 2

Ответ заключается в том, что Go вставит в скомпилированный код логику проверки выхода индекса за пределы, поэтому эффективность доступа к индексу массива будет скомпрометирована, что несопоставимо с производительностью доступа к массиву языка C.

присвоение массива

Массивы одного и того же типа подэлемента и одинаковой длины могут быть назначены друг другу, в противном случае они относятся к разным типам массивов и не могут быть назначены. Присвоение массива по сути представляет собой операцию поверхностного копирования, и значения двух назначенных переменных массива не будут использоваться совместно.

package main

import "fmt"

func main() {
	var a = [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	var b [9]int
	b = a
	a[0] = 12345
	fmt.Println(a)
	fmt.Println(b)
}

--------------------------
[12345 2 3 4 5 6 7 8 9]
[1 2 3 4 5 6 7 8 9]

Из результатов выполнения приведенного выше кода видно, что два массива не имеют общих внутренних элементов после присваивания. Если длина массива очень велика, операция копирования имеет определенные накладные расходы, и вы должны быть внимательны при ее использовании. Далее мы пытаемся использовать присваивания массивов разной длины, что произойдет?

package main

import "fmt"

func main() {
	var a = [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	var b [10]int
	b = a
	fmt.Println(b)
}

--------------------------
./main.go:8:4: cannot use a (type [9]int) as type [10]int in assignment

Видно, что присваивание между массивами разной длины запрещено, поскольку они относятся к разным типам.

обход массива

Помимо использования индексов для обхода массива, для обхода также можно использовать ключевое слово range.Обход диапазона предоставляет следующие две формы.

package main

import "fmt"

func main() {
	var a = [5]int{1,2,3,4,5}
	for index := range a {
        fmt.Println(index, a[index])
    }
    for index, value := range a {
		fmt.Println(index, value)
	}
}

------------
0 1
1 2
2 3
3 4
4 5
0 1
1 2
2 3
3 4
4 5

Учитывая, что в слайсах слишком много контента, мы обсудим слайсы в отдельном разделе, а следующий раздел будет очень ценным разделом языка Go, читатели должны постараться понять каждую деталь.

Отсканируйте приведенный выше QR-код, чтобы прочитать дополнительные главы «Быстрое изучение языка Go».