Вы находитесь на странице: 1из 62

Golang

Массивы и слайсы

2020
2
Информация
Данная публикация стала возможной благодаря помощи американского народа,

оказанной через Агентство США по международному развитию (USAID). Алиф

Академия несёт ответственность за содержание публикации, которое не

обязательно отражает позицию USAID или Правительства США.


ПРЕДИСЛОВИЕ
4
Предисловие
До этого мы с вами работали с 1-2 однотипными сущностями, например, теми же
картами. И для нас не составляло проблемы хранить всё в одной или двух
переменных.

Но что если таких сущностей (будем называть их объектами) станет 10 или 100?
Уже будет достаточно проблемно с этим справиться. Давайте смотреть, что Go
предлагает нам для этого.
МАССИВЫ
6
Массивы
Например, у нас есть история операций по карте (см. скриншот). Понятно, что мы
не будем заводить несколько сотен переменных под это.
7
Массивы
Вместо этого мы как в жизни, хотели бы сказать "Операции" и хранить там все

операции. И просто говорить: "первая операция", "последняя операция", а,

возможно, сделать ещё как-то удобнее.


8
Массивы
В Go для хранения набора элементов существуют массивы. Массив - это

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

слов, давайте по порядку разбираться, что каждое из них значит:

1. Упорядоченный - это значит, у элементов есть порядок, т.е. можно чётко

сказать, который из них первый, а который последний

2. Одного типа - это значит, что к элементам "операций" нельзя положить что-то

другое, что не является операцией по карте

3. Фиксированной - это значит, что нельзя изменить количество элементов


9
Массивы
Начнём с простого - попробуем хранить только суммы операций (а потом уже

структуры):
10
Массивы
Общая структура:

● объявление: [кол-во элементов]тип элементов

● элементы нумеруются с 0 до кол-во элементов - 1

● порядковый номер элемента называют индексом

● индекс может быть от 0 до кол-во элементов - 1

● количество элементов в массиве называют размером массива

● чтобы записать в конкретный элемент: имя[индекс] = значение

● чтобы прочитать значение: имя[индекс]

● чтобы узнать размер: len(имя) (len - это функция из builtin)


11
Массивы
Вариант без var:
12
Инициализация
Мы знаем, что обычные переменные при инициализации инициализируются в

нулевые значения (числа в 0, строки в ""), а в структурах - поля инициализируются

в нулевые значения типов полей.

В массивах всё так же: все

элементы

инициализируются в

нулевые значения типа:


13
Ошибки
Теперь давайте посмотрим, что произойдёт, если мы вдруг напишем

неправильный индекс:

Наше приложение буквально

"обрушится" (т.е. завершится

с ошибкой).

Вам нужно запомнить эту

ошибку, чтобы понимать, что

привело к её появлению.
14
Инициализация
Мы можем сразу инициализировать массив значениями при объявлении:
15
Инициализация
И есть вариант "для ленивых", когда мы просим компилятор сосчитать, сколько же

элементов мы написали в фигурных скобках:


ЦИКЛЫ
17
Циклы
Просто так объявлять массивы и работать с ними по индексу не особо интересно,

поскольку это "почти" равносильно тому, что мы бы сделали много переменных.

Пока выгода только в том, что мы все элементы сохранили под одним именем, но

разными индексами.

Основная же мощь массивов - это их совместное использование с циклами.


18
Циклы
Цикл - это блок кода, который повторяется несколько раз. Давайте вернёмся к

нашему примеру - мы хотели распечатать на экран все траты (самая старая -

самая первая):

В реальной жизни, мы так не говорим

("распечатай первую, вторую, третью"), мы

говорим "распечатай каждую".

Давайте смотреть, как это выглядит в Go.


19
Циклы
В Go есть всего один цикл - называется for. Первая его форма выглядит

следующим образом:

for <инициализация>; <условие продолжения>; <пост.операция> {

// повторяющийся блок кода

В VS Code для этого есть snippet for:


20
Циклы
Общая идея: мы создаём переменную (в секции инициализации), значение

которой затем меняем (в секции пост.операции). Пока условие продолжения

вычисляется в true, Go продолжает выполнение цикла, перед каждым проходом

проверяя условие продолжения, а после каждого цикла - пост.операцию.

Тонкий момент: переменная, созданная в секции инициализации не доступна за

пределами for (т.е. доступна только в строке самого for + в теле цикла)
21
Циклы
Сначала это может показаться сложным, но давайте смотреть на примере:

Что произошло?

1. Мы создали переменную i := 0

2. Проверили, что i (которое равно 0, меньше len(operations), которое равно 3)

3. Выполнили код на 11 строке

4. Выполнили i++ (i++ - это сокращение для i = i + 1, т.е. теперь i = 1)

5. Переходим на шаг 2, но теперь i = 1


22
Циклы
Поставим точку остановки внутри цикла (это {} после for) и пройдём по шагам.
23
Шаг 1
i := 0, i < len(operations) = true
24
Шаг 2
i := 1, i < len(operations) = true
25
Шаг 3
i := 2, i < len(operations) = true
26
Шаг 4
i := 3, i < len(operations) = false

Поэтому Go не заходит в тело цикла, а идёт на следующую строку после цикла

(строка 12). Там завершается функция main, соответственно, и завершается наша

программа.
27
Циклы
Если переводить циклы в обычный последовательный код, то он будет выглядеть

вот так (при условии, что в operations 3 элемента):


28
Циклы
Пойдём чуть дальше: в качестве индекса мы можем использовать не только

константу, но и переменную:
29
Циклы
А это значит, что мы можем прямо в цикле перебирать все значения массива:
30
Циклы
Это операция настолько частая (перебор массива с выбором элемента по

индексу), что в Go для этой цели придумали специальную конструкцию for range:
31
Циклы
Выражение справа возвращает два значения: индекс и сам элемент. Если индекс

нам не нужен, то мы можем использовать специальное имя - _ (blank identifier),

чтобы Go не ругался на неиспользуемые переменные:

Обратите внимание: for range сам остановится, когда переберёт все элементы в

массиве.
32
Циклы
Здесь нужно отметить, что range - это не функция, а конструкция языка, поэтому

слева от неё можно писать как два имени (index, operation или _, operation, если

index нам не нужен), так и одно - index (т.е. в этом случае вам будет передаваться

только индекс и никакой ошибки не будет).


33
Типичная ошибка
Важно: если вам нужен только элемент, не используйте индекс:

Такая запись будет показывать вашу низкую квалификацию как Go разработчика.


КЛЮЧЕВЫЕ ОПЕРАЦИИ
35
Ключевые операции
При работе с набором элементов существует ряд ключевых операций:

1. Добавление элемента

2. Удаление элемента

3. Поиск (одного, нескольких или всех)


рассмотрим сегодня
4. Агрегация (подсчёт суммы, среднего и т.д.)
36
Сумма
Давайте пойдём снизу-вверх, начиная с агрегирующих функций. Самая простая и

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

этом месяце (пусть у нас хранятся операции только за месяц). Для этого

достаточно просуммировать все операции:

Это типичный шаблон. Работает он

примерно так же, как мы обычно

считаем сумму - начинаем с нуля, а

потом прибавляем каждый элемент.

Это нужно просто запомнить.


37
Максимум
Теперь интереснее: нам интересно посмотреть на максимальную операцию. И

здесь сразу встаёт куча вопросов: максимальную - это с положительной суммой

или по модулю (т.е. по величине, не важно, положительная или отрицательная)? А

если есть две операции с одинаковой суммой, мы берём первую или последнюю?

Это достаточно сложные вопросы, от ответа на которые зависит, правильно вы в

итоге напишите программу или нет.

Мы пока рассмотрим вариант с поиском максимума с учётом знака (т.е. -10 и 9

дирам меньше 10 дирам, значит 10 дирам - будет максимальной суммой) и будет

искать последний.
38
Максимум
Операция эта тоже шаблонная: мы берём первый элемент и сравниваем все

остальные с ним. Как только один из этих "остальных" становится больше или

равен того, что у нас сейчас есть - мы выкидываем тот, который у нас есть и берём

тот, с которым сейчас сравнивали:

Обязательно пройдитесь

дебаггером по этому коду и

удостоверьтесь, что вы понимаете,

как он работает.
39
Ключевые операции
Но если это настолько частые и

шаблонные операции, почему бы

нам не сделать функции, которые

хотя бы для чисел умеют это

делать?

Давайте попробуем. Оно даже

будет работать!
40
Ключевые операции
Проблемы у нашего решения ровно две:

1. При передаче массива в качестве аргумента функции происходит

копирование всех элементов массива

2. Тип массива - это количество элементов и их тип, т.е. в наши функции мы

можем передавать массивы только размером 3, а для размера 4 придётся

писать отдельные функции

И если с первым мы ещё можем что-то поделать, то вот со вторым - уже ничего

поделать не можем, наших знаний пока не хватает. А значит, нам нужно искать

более удобный инструмент.


СЛАЙСЫ
42
Слайсы
Напрямую массивы используются очень редко - только если вы знаете заранее
количество элементов. Чаще же, используется специальный тип - слайс. Это некая
магическая оболочка* над массивом, которая:
● содержит указатель на массив, поэтому копировать её не страшно (сам
массив элементов скопирован не будет, будет скопирован только указатель
на него)
● умеет "расширяться" - т.е. можно в него добавлять дополнительные элементы
с помощью специальной функции append
● может быть использован в циклах так же, как и массив
● поддерживает ряд других операций (пройдём позже)

Как слайс устроен внутри, мы с вами рассмотрим на следующих лекциях, пока же


разберёмся, как им пользоваться.
43
Объявление
Во-первых, слайс нужно объявить:

Как вы видите, всё отличие объявления слайса от объявления массива - это


отсутствие указания размера, поскольку размер может меняться.

Важно: не путайте слайс и массив. Не называйте слайс массивом и массив


слайсом.
44
Объявление
Тот слайс, который мы объявили - называется nil-слайсом. Надеемся, что вы
помните про nil из лекции про указатели. Это значит, что в нём нет данных.
Поэтому любые обращения по любым индексам предсказуемо приведут к
ошибкам:
45
Объявление
Поэтому прежде чем обращаться к элементам, нужно их добавить. Сделать это
можно тремя способами:
1. Инициализировать слайс, заполнив его нулевыми значениями
2. Инициализировать слайс не нулевыми значениями
3. Добавить необходимое количество элементов с помощью функции append

Давайте рассмотрим по порядку.


46
Вариант 1
Используется специальная функция make, которая создаёт слайс и массив из 10
элементов для него (массив мы не видим, так как он скрыт внутри слайса):
47
Вариант 2
Используется выражение инициализации (не путайте с массивом - здесь мы
ничего не пишем в квадратных скобках):
48
Вариант 3
Используется builtin функция append. Обратите внимание: append мы пока будем
использовать именно так - присваивать результат обратно той же переменной:
49
make & append
Нужно отметить, что функции make и append встроенные (т.е. в builtin.go), поэтому
их импортировать не нужно.
50
Слайсы
Теперь дело за малым - нужно всего лишь

переписать наши функции.

Всё, что мы сделали - это убрали размер и

теперь наши функции принимают не

массивы, а слайсы. При этом не нужно

передавать указатель на слайсы - слайсы

очень "лёгкие".
ИТОГИ
52
Итоги
В этой лекции мы обсудили тип, который вы будете использовать чаще всего в Go -
это слайсы. Мы обсудили самые верхнеуровневые моменты, которые позволят
вам уже сейчас их использовать, не вдаваясь в детали внутренней реализации
(которые мы, конечно же, ещё будем рассматривать).
ДОМАШНЕЕ ЗАДАНИЕ
54
Орг.моменты
Курс состоит из 33 обязательных занятий. Мы будем проводить онлайн-вебинары
каждую среду и пятницу в 18:30 (по Душанбе). По итогу онлайн-вебинара мы
выкладываем PDF-материал.

В течении 24 часов мы выкладываем запись вебинара. Каждый вторник 12:00 дедлайн


сдачи домашнего задания.

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

Все вопросы вы сможете задавать в телеграмм-чате https://t.me/academy_go_chat


55
ДЗ №1 Total
Мы можем делать слайсы не только из чисел, но и из структур. Вот так будет выглядеть
объявление слайса из двух структур:

Кстати, сниппет для создания example: ef + Tab.


56
ДЗ №1 Total
Эту запись можно сократить, т.к. итак понятно, что в слайсе из карт могут храниться
только карты:
57
ДЗ №1 Total
Мы хотим, чтобы вы написали функцию Total, которая высчитывает сумму денег на всех
ваших картах (ваши карты в слайсе).

Эту задачу вам поставил более старший программист, вся постановка задачи
заключается в следующем:

Вам нужно написать её реализацию.


58
ДЗ №1 Total
Функция должна располагаться в пакете pkg/bank/card файле card.go. Все типы
должны соответствовать предыдущему заданию и лекции.
59
ДЗ №2 Max Payment
Как вы уже поняли, по каждой карте можно совершать платежи. И мы решили хранить
платежи в следующей структуре (пакет types):

Соответственно, когда платежей становится много, их можно хранить также в слайсе:


60
ДЗ №2 Max Payment
Соответственно, так же, как и в первой ДЗ, вам нужно реализовать функцию:
61
ДЗ №2 MaxPayment
Функция должна располагаться в пакете pkg/bank/payment файле payment.go. Все
типы должны соответствовать предыдущему заданию и лекции.

Если у некоторых платежей совпадут суммы, то нужно вернуть тот, который


встретится вам самым первым.
Спасибо за внимание

alif academy

2020