Бесплатный пробный урок: Golang Developer
Пройдите пару настоящих уроков этого курса — регистрация не требуется.
Урок 1: Пакеты, комментарии, переменные и операторы
Пакеты. Комментарии.
Любой код в Go хранится внутри пакета. Пакет — это папка, которая содержит файлы .go.
Давайте рассмотрим первый простой пример создания программы на Go:
package main
import "fmt"
func main() {
fmt.Println("Salem Bitlab!")
}
Salem Bitlab!Для выполнения данной программы мы использовали пакеты main и fmt.
package main — это главный пакет. Он обязательно должен содержать функцию func main(), которая является точкой входа в программу. При запуске Go-приложения именно эта функция вызывается первой.
package fmt — стандартный пакет, который используется для вывода данных в консоль. Помимо вывода, он также поддерживает форматированный ввод и вывод данных.
Когда программа становится больше, читать и понимать код становится сложнее. Чтобы упростить понимание логики программы, используются комментарии.
Комментарии нужны для пояснения кода и его логического разделения. Они не участвуют в компиляции и никак не влияют на выполнение программы.
Виды комментариев
Однострочные комментарии
package main
import "fmt"
func main() {
fmt.Println("I like Bitlab Academy!") // вывод текста в консоль
}
I like Bitlab Academy!Однострочный комментарий используется для краткого пояснения конкретной строки или части кода.
Многострочные комментарии
package main
import "fmt"
/*
В данной программе
при помощи пакета "fmt"
мы отображаем данные
в консоль
*/
func main() {
fmt.Println("I like Bitlab Academy!")
}
I like Bitlab Academy!Многострочные комментарии используются для подробного описания функций, структур и больших логических блоков кода.
Переменные и типы данных
Go — статически типизированный язык программирования. Компилятор Go перед запуском программы проверяет типы данных, что обеспечивает высокую надёжность, производительность и читаемость кода.
Go — настолько быстрый язык, что я зову его Флэш в мире программирования.
— Сусликов Битлабхан, признанный обществом деятель в области обретения полезных навыков
Перед тем как приступить к знакомству с типами данных, необходимо понять, что такое переменные.
Переменные — это участок памяти, который хранит некоторое значение. Каждая переменная имеет тип данных, который определяет, какие операции с ней можно выполнять.
Объявление переменной выглядит следующим образом:
package main
import "fmt"
func main() {
var age int = 22
fmt.Println(age)
}
22Для объявления переменной мы используем ключевое слово var, затем имя переменной и её тип данных. После этого мы можем свободно использовать переменную в программе.
В Go также существует краткая форма объявления переменной:
package main
import "fmt"
func main() {
age := 22
fmt.Println(age)
}
22При использовании оператора := Go автоматически определяет тип переменной на основе присваиваемого значения.
Основные типы данных в Go
Для работы с числами:
int— целые положительные и отрицательные числаuint— только положительные целые числаfloat32— дробные числа (32 бита)float64— дробные числа (64 бита)complex64— комплексные числа (2 × float32)complex128— комплексные числа (2 × float64)
Для работы со строками:
string— текстовые данные
Для логических операций:
bool— true или false
Для работы с наборами данных:
array— массив фиксированной длиныslice— массив динамической длиныmap— структура ключ–значениеstruct— объединение разных типов данныхinterface— абстрактный тип данных
Дополнительные типы данных:
pointer— указательfunction— функция как тип данныхchannel— взаимодействие между горутинами
Математические операторы
Математические операторы позволяют выполнять операции с числами.
Сложение (+)
a := 2
b := 2
c := a + b // 4
Вычитание (-)
a := 10
b := 5
c := a - b // 5
Умножение (*)
a := 3
b := 3
c := a * b // 9
Деление (/)
a := 6
b := 2
c := a / b // 3
Остаток от деления (%)
a := 7
b := 3
c := a % b // 1
Инкремент (++)
a := 7
a++ // 8
Декремент (--)
a := 7
a-- // 6
Чтобы закрепить изученный материал, рекомендуется попрактиковаться, решая подготовленные задачи. Удачи!
Сумма квадратов
SumOfSquares(a int, b int) int, которая возвращает a*a + b*b.SumOfSquares(2, 2) → 8SumOfSquares(3, 4) → 25-
Ввод:
SumOfSquares(2, 2) -
Ввод:
SumOfSquares(3, 4) -
Ввод:
SumOfSquares(1, 1) -
Ввод:
SumOfSquares(0, 0) -
Ввод:
SumOfSquares(-2, -2)
Последняя цифра
LastDigit(n int) int, которая возвращает его последнюю цифру.LastDigit(123) → 3LastDigit(0) → 0-
Ввод:
LastDigit(123) -
Ввод:
LastDigit(0) -
Ввод:
LastDigit(9) -
Ввод:
LastDigit(100) -
Ввод:
LastDigit(4567)
Число десятков
TensDigit(n int) int, которая находит число десятков (вторую цифру справа).TensDigit(2010) → 1TensDigit(0) → 0-
Ввод:
TensDigit(2010) -
Ввод:
TensDigit(0) -
Ввод:
TensDigit(5) -
Ввод:
TensDigit(99) -
Ввод:
TensDigit(12345)
Страна и город
Describe(country string, city string) string, которая возвращает название страны и города в формате, как показано в примерах.Describe("Kazakhstan", "Astana") → Country: Kazakhstan
City: AstanaDescribe("France", "Paris") → Country: France
City: Paris-
Ввод:
Describe("Kazakhstan", "Astana") -
Ввод:
Describe("France", "Paris") -
Ввод:
Describe("Japan", "Tokyo") -
Ввод:
Describe("USA", "New York") -
Ввод:
Describe("Germany", "Berlin")
Вычисление формулы
Evaluate(x int) int, которая находит значение y = 4(x - 3) - 7(x - 3) + 2 при данном x.Evaluate(4) → -1Evaluate(-4) → 23-
Ввод:
Evaluate(4) -
Ввод:
Evaluate(-4) -
Ввод:
Evaluate(3) -
Ввод:
Evaluate(0) -
Ввод:
Evaluate(10)
Урок 2: Условные конструкции и логические операторы
Что такое условия в Go?
В реальных программах код не всегда выполняется сверху вниз одинаково. Иногда программа должна принять решение: выполнить один блок кода или другой.
Например:
- если возраст пользователя больше или равен 18 — разрешить доступ;
- если пароль правильный — войти в систему;
- если баланс достаточный — выполнить оплату;
- если оценка высокая — вывести сообщение об успехе.
Для таких ситуаций в Go используются условные конструкции. Они позволяют программе выбирать, какой код нужно выполнить.
Условие — это выражение, результатом которого всегда является true или false.
Как работает условие?
Условная конструкция проверяет выражение. Если результат выражения равен true, выполняется один блок кода. Если результат равен false, этот блок пропускается или выполняется другой блок.
Конструкция if
Самая простая условная конструкция — это if. Она выполняет код только в том случае, если условие истинно.
package main
import "fmt"
func main() {
age := 20
if age >= 18 {
fmt.Println("Доступ разрешён")
}
}
Доступ разрешёнЗдесь программа проверяет условие age >= 18. Значение переменной age равно 20, поэтому условие истинно. Значит, код внутри блока if выполнится.
Важно: в Go условие внутри if не пишется в круглых скобках. Но фигурные скобки { } обязательны.
Конструкция if ... else
Если нужно выполнить один код при истинном условии, а другой код при ложном условии, используется конструкция if ... else.
package main
import "fmt"
func main() {
age := 15
if age >= 18 {
fmt.Println("Доступ разрешён")
} else {
fmt.Println("Доступ запрещён")
}
}
Доступ запрещёнЗначение age равно 15. Условие age >= 18 возвращает false. Поэтому блок if не выполняется, а выполняется блок else.
Несколько условий: else if
Иногда двух вариантов недостаточно. Например, нужно проверить оценку студента и вывести разный результат. В таком случае используется else if.
package main
import "fmt"
func main() {
score := 85
if score >= 90 {
fmt.Println("Отлично")
} else if score >= 75 {
fmt.Println("Хорошо")
} else if score >= 50 {
fmt.Println("Можно лучше")
} else {
fmt.Println("Не сдал")
}
}
ХорошоПрограмма проверяет условия сверху вниз:
score >= 90 — false
score >= 75 — true
выводится Хорошо
остальные условия уже не проверяются
Операторы сравнения
Чтобы строить условия, часто используются операторы сравнения. Они сравнивают значения и возвращают результат true или false.
==— равно!=— не равно>— больше<— меньше>=— больше или равно<=— меньше или равно
package main
import "fmt"
func main() {
a := 10
b := 5
fmt.Println(a == b)
fmt.Println(a != b)
fmt.Println(a > b)
fmt.Println(a < b)
}
false
true
true
falseЛогические операторы
Логические операторы позволяют объединять несколько условий. Это полезно, когда одного сравнения недостаточно.
&&— И, оба условия должны быть true||— ИЛИ, хотя бы одно условие должно быть true!— НЕ, меняет true на false и false на true
Если нужно проверить несколько правил одновременно, чаще всего используются операторы && и ||.
Пример с оператором &&
Оператор && означает, что оба условия должны быть истинными. Например, пользователь может войти в систему только если логин и пароль правильные.
package main
import "fmt"
func main() {
login := "admin"
password := "12345"
if login == "admin" && password == "12345" {
fmt.Println("Добро пожаловать")
} else {
fmt.Println("Неверный логин или пароль")
}
}
Добро пожаловатьПример с оператором ||
Оператор || означает, что достаточно выполнения хотя бы одного условия. Например, город может называться Astana или Nur-Sultan.
package main
import "fmt"
func main() {
city := "Astana"
if city == "Astana" || city == "Nur-Sultan" {
fmt.Println("Здравствуй столица")
} else {
fmt.Println("Это другой город")
}
}
Здравствуй столицаПример с оператором !
Оператор ! меняет значение логического выражения на противоположное. Если было true, станет false. Если было false, станет true.
package main
import "fmt"
func main() {
isBlocked := false
if !isBlocked {
fmt.Println("Пользователь активен")
} else {
fmt.Println("Пользователь заблокирован")
}
}
Пользователь активенИнициализация внутри if
В Go можно создать переменную прямо внутри конструкции if. Такая переменная будет доступна только внутри этого условия.
package main
import "fmt"
func main() {
if age := 19; age >= 18 {
fmt.Println("Совершеннолетний")
} else {
fmt.Println("Несовершеннолетний")
}
}
СовершеннолетнийПеременная age создана перед проверкой условия. После завершения конструкции if ... else использовать эту переменную уже нельзя.
Конструкция switch ... case
Если нужно сравнить одно значение с несколькими вариантами, удобно использовать конструкцию switch ... case.
Она делает код чище, когда вариантов много.
package main
import "fmt"
func main() {
day := 3
switch day {
case 1:
fmt.Println("Понедельник")
case 2:
fmt.Println("Вторник")
case 3:
fmt.Println("Среда")
case 4:
fmt.Println("Четверг")
case 5:
fmt.Println("Пятница")
default:
fmt.Println("Выходной или неизвестный день")
}
}
СредаВ Go после каждого case не нужно писать break. После выполнения найденного варианта конструкция switch завершается автоматически.
default выполняется тогда, когда ни один case не подошёл.
switch без значения
В Go можно использовать switch без конкретного значения. В таком случае каждый case становится отдельным условием.
package main
import "fmt"
func main() {
temperature := 30
switch {
case temperature >= 30:
fmt.Println("Жарко")
case temperature >= 20:
fmt.Println("Тепло")
case temperature >= 10:
fmt.Println("Прохладно")
default:
fmt.Println("Холодно")
}
}
ЖаркоПрактические примеры
Чтобы лучше понять условия, полезно решать простые задачи.
- Проверить, является ли число положительным или отрицательным.
- Проверить, является ли число чётным или нечётным.
- Проверить, может ли пользователь получить доступ по возрасту.
- Проверить логин и пароль пользователя.
- Вывести название дня недели по номеру.
Итоги
Условные конструкции позволяют программе принимать решения. Без условий невозможно написать полноценную программу, потому что почти любая логика зависит от разных ситуаций.
ifиспользуется для простой проверки;elseвыполняется, если условие ложно;else ifнужен для проверки нескольких вариантов;switchудобен, когда нужно сравнить значение с несколькими вариантами;&&,||,!позволяют объединять и изменять условия.
Положительное, отрицательное или ноль
Positive, если отрицательное — Negative. Если подаётся ноль — вывести Zero. Напишите функцию Sign(n int) string, которая возвращает сообщение (без кавычек).Sign(5) → PositiveSign(-13) → Negative-
Ввод:
Sign(5) -
Ввод:
Sign(-13) -
Ввод:
Sign(0) -
Ввод:
Sign(1) -
Ввод:
Sign(-100)
Все цифры разные?
AllDigitsDifferent(n int) string, которая возвращает YES, если все его цифры различны, и NO в противном случае.AllDigitsDifferent(237) → YESAllDigitsDifferent(117) → NO-
Ввод:
AllDigitsDifferent(237) -
Ввод:
AllDigitsDifferent(117) -
Ввод:
AllDigitsDifferent(123) -
Ввод:
AllDigitsDifferent(111) -
Ввод:
AllDigitsDifferent(989)
Какая это школа?
SchoolByGrade(grade int) string. С 1-го по 4-ый включительно классы — это Beginners School, с 5-го по 9-ый включительно — Middle School, а с 10-го по 11-ый включительно — High School.SchoolByGrade(2) → Beginners SchoolSchoolByGrade(10) → High School-
Ввод:
SchoolByGrade(2) -
Ввод:
SchoolByGrade(10) -
Ввод:
SchoolByGrade(1) -
Ввод:
SchoolByGrade(9) -
Ввод:
SchoolByGrade(11)
Большее из двух чисел
MaxOfTwo(a int, b int) int, которая возвращает значение наибольшего из них.MaxOfTwo(17, 81) → 81MaxOfTwo(872, 976) → 976-
Ввод:
MaxOfTwo(17, 81) -
Ввод:
MaxOfTwo(872, 976) -
Ввод:
MaxOfTwo(5, 5) -
Ввод:
MaxOfTwo(-10, -20) -
Ввод:
MaxOfTwo(100, 1)
Наибольшее из трёх чисел
MaxOfThree(a int, b int, c int) int, которая принимает три целых числа и возвращает наибольшее среди них.MaxOfThree(17, 18, 19) → 19MaxOfThree(89, 221, 19) → 221-
Ввод:
MaxOfThree(17, 18, 19) -
Ввод:
MaxOfThree(89, 221, 19) -
Ввод:
MaxOfThree(5, 5, 5) -
Ввод:
MaxOfThree(-1, -2, -3) -
Ввод:
MaxOfThree(0, 100, 50)
Урок 3: Циклы и оператор for
Что такое цикл?
Цикл — это последовательность инструкций, которая позволяет выполнить некоторые действия множество раз.
В программировании очень часто бывают ситуации, когда одно и то же действие нужно повторить несколько раз. Например:
- вывести числа от 1 до 10;
- посчитать сумму чисел;
- прочитать несколько значений с консоли;
- обработать список студентов;
- напечатать таблицу умножения.
Без циклов пришлось бы писать одинаковый код много раз вручную. Циклы позволяют сделать код короче, понятнее и удобнее.
В Go есть всего один цикл — for, но он может принимать разные формы.
Как работает цикл?
- сначала задаётся начальное значение;
- затем проверяется условие;
- если условие истинно, выполняется тело цикла;
- после этого значение изменяется;
- затем условие проверяется снова.
Классический for
Классический цикл for используется тогда, когда мы заранее понимаем, сколько примерно раз должен выполниться цикл.
Например, если нужно вывести числа от 0 до 4:
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
fmt.Println(i)
}
}
0
1
2
3
4Конструкция цикла состоит из трёх частей:
i := 0— инициализация (стартовое значение)i < 5— условие (пока истинно — цикл работает)i++— шаг (изменение переменной после каждой итерации)
Таким образом цикл сработал 5 раз — пока i не стал равен 5.
Пример: вывести числа от 1 до 10
Очень частая задача — вывести числа в определённом диапазоне. Для этого можно использовать обычный цикл for.
package main
import "fmt"
func main() {
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
}
1
2
3
4
5
6
7
8
9
10В этом примере переменная i начинается с 1. Цикл работает до тех пор, пока i <= 10. После каждой итерации значение i увеличивается на 1.
Пример: сумма чисел
Циклы часто используются для накопления результата. Например, можно посчитать сумму чисел от 1 до 5.
package main
import "fmt"
func main() {
sum := 0
for i := 1; i <= 5; i++ {
sum = sum + i
}
fmt.Println(sum)
}
15Здесь переменная sum хранит общий результат. На каждой итерации мы добавляем к ней текущее значение i.
Как меняется sum:
- i = 1, sum = 0 + 1 = 1
- i = 2, sum = 1 + 2 = 3
- i = 3, sum = 3 + 3 = 6
- i = 4, sum = 6 + 4 = 10
- i = 5, sum = 10 + 5 = 15
Пример: вывести только чётные числа
Внутри цикла можно использовать условные конструкции. Например, можно пройтись по числам от 1 до 10 и вывести только чётные числа.
package main
import "fmt"
func main() {
for i := 1; i <= 10; i++ {
if i%2 == 0 {
fmt.Println(i)
}
}
}
2
4
6
8
10Условие i%2 == 0 проверяет, делится ли число на 2 без остатка. Если остаток равен 0, значит число чётное.
Форма for как while
В Go нет отдельного ключевого слова while. Но цикл for можно использовать как while, если оставить только условие.
package main
import "fmt"
func main() {
i := 1
for i < 10 {
fmt.Println(i)
i++
}
}
1
2
3
4
5
6
7
8
9Как и прежде — цикл работает до тех пор, пока условие истинно. В данном случае — пока i < 10.
Бесконечный цикл
Если написать for без условия, получится бесконечный цикл. Такой цикл будет выполняться бесконечно, пока мы не остановим его вручную или не используем break.
package main
import "fmt"
func main() {
i := 1
for {
fmt.Println(i)
i++
if i > 5 {
break
}
}
}
1
2
3
4
5Важно: бесконечный цикл нужно использовать аккуратно. Если забыть условие выхода, программа может работать бесконечно.
Оператор break
Оператор break позволяет досрочно завершить цикл. Он полезен, когда мы нашли нужное значение и больше не хотим продолжать выполнение цикла.
package main
import "fmt"
func main() {
for i := 1; i <= 10; i++ {
if i == 6 {
break
}
fmt.Println(i)
}
}
1
2
3
4
5Когда i становится равным 6, срабатывает break. Цикл сразу завершается, поэтому число 6 и следующие числа не выводятся.
Оператор continue
Оператор continue пропускает текущую итерацию и сразу переходит к следующей. Он полезен, когда некоторые значения нужно пропустить.
package main
import "fmt"
func main() {
for i := 1; i <= 5; i++ {
if i == 3 {
continue
}
fmt.Println(i)
}
}
1
2
4
5Когда i равно 3, выполняется continue. Поэтому fmt.Println(i) для числа 3 пропускается.
Цикл с вводом данных
Циклы можно использовать для чтения данных с консоли. Например, можно вводить числа до тех пор, пока пользователь не введёт 0.
package main
import "fmt"
func main() {
var number int
for {
fmt.Scan(&number)
if number == 0 {
break
}
fmt.Println(number)
}
}
В этом примере цикл работает бесконечно, но завершается, когда пользователь вводит число 0.
Такой подход часто используется, когда заранее неизвестно, сколько значений введёт пользователь.
Вложенные циклы
Цикл может находиться внутри другого цикла. Такой подход называется вложенными циклами.
Один из самых популярных примеров — таблица умножения.
package main
import "fmt"
func main() {
for i := 1; i < 10; i++ {
for j := 1; j < 10; j++ {
fmt.Print(i*j, "\t")
}
fmt.Println()
}
}
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81Получилась таблица умножения: по горизонтали идёт i, по вертикали — j. Каждый из циклов работает, пока переменные меньше 10.
Как это работает:
- внешний цикл отвечает за строки;
- внутренний цикл отвечает за столбцы;
- на каждой итерации выводится результат i * j;
- после завершения внутреннего цикла выполняется переход на новую строку.
Пример вложенного цикла: прямоугольник из звёздочек
Вложенные циклы часто используются для рисования простых фигур в консоли. Например, можно вывести прямоугольник из символов *.
package main
import "fmt"
func main() {
for i := 1; i <= 3; i++ {
for j := 1; j <= 5; j++ {
fmt.Print("*")
}
fmt.Println()
}
}
*****
*****
*****Внешний цикл выполняется 3 раза и создаёт 3 строки. Внутренний цикл выполняется 5 раз и печатает 5 звёздочек в каждой строке.
Цикл for range
В Go часто используется форма for range. Она удобна, когда нужно пройтись по набору данных.
Например, можно пройтись по списку имён:
package main
import "fmt"
func main() {
names := []string{"Aibek", "Dana", "Miras"}
for index, name := range names {
fmt.Println(index, name)
}
}
0 Aibek
1 Dana
2 MirasПеременная index хранит номер элемента, а переменная name хранит само значение.
Если индекс не нужен
Иногда нам нужно только значение, а индекс не нужен. В таком случае вместо индекса можно использовать символ _.
package main
import "fmt"
func main() {
names := []string{"Aibek", "Dana", "Miras"}
for _, name := range names {
fmt.Println(name)
}
}
Aibek
Dana
MirasСимвол _ означает, что это значение нам не нужно.
Частые ошибки при работе с циклами
- забыть изменить переменную цикла;
- написать условие, которое всегда истинно;
- создать бесконечный цикл без break;
- неправильно указать границы цикла;
- перепутать < и <=.
Мини-практика
Чтобы лучше закрепить циклы, попробуйте написать программы для следующих задач:
- вывести числа от 1 до 100;
- вывести только нечётные числа от 1 до 50;
- посчитать сумму чисел от 1 до N;
- вывести таблицу умножения для одного числа;
- вводить числа до тех пор, пока пользователь не введёт 0;
- найти максимальное число среди введённых чисел;
- вывести квадрат из символов *.
Итоги
Циклы позволяют выполнять один и тот же код много раз. В Go для этого используется только цикл for, но он может быть записан в разных формах.
- классический
forудобен, когда известны границы цикла; forкакwhileудобен, когда важна только проверка условия;- бесконечный
forиспользуется вместе сbreak; breakзавершает цикл;continueпропускает текущую итерацию;- вложенные циклы позволяют работать с таблицами и фигурами;
for rangeиспользуется для прохода по наборам данных.
Считаем от n до m
RangeString(n int, m int) string, которая возвращает все числа от n до m через пробел.RangeString(5, 10) → 5 6 7 8 9 10RangeString(2, 9) → 2 3 4 5 6 7 8 9-
Ввод:
RangeString(5, 10) -
Ввод:
RangeString(2, 9) -
Ввод:
RangeString(1, 3) -
Ввод:
RangeString(7, 7) -
Ввод:
RangeString(4, 1)
Чётные числа до n
EvensUpTo(n int) string, которая возвращает все ЧЁТНЫЕ числа от 0 до n через пробел (чётные числа делятся на 2 без остатка).EvensUpTo(6) → 0 2 4 6EvensUpTo(10) → 0 2 4 6 8 10-
Ввод:
EvensUpTo(6) -
Ввод:
EvensUpTo(10) -
Ввод:
EvensUpTo(0) -
Ввод:
EvensUpTo(1) -
Ввод:
EvensUpTo(7)
Факториал через for
Factorial(n int) int, которая возвращает факториал n, вычисленный с помощью цикла for.Factorial(5) → 120Factorial(0) → 1-
Ввод:
Factorial(5) -
Ввод:
Factorial(0) -
Ввод:
Factorial(1) -
Ввод:
Factorial(3) -
Ввод:
Factorial(6)
Числа и их квадраты
NumbersAndSquares(n int) string, которая возвращает все числа от 1 до n вместе с их квадратами, по одной паре на строку, как показано в примерах.NumbersAndSquares(3) → 1 1
2 4
3 9NumbersAndSquares(5) → 1 1
2 4
3 9
4 16
5 25-
Ввод:
NumbersAndSquares(3) -
Ввод:
NumbersAndSquares(5) -
Ввод:
NumbersAndSquares(1) -
Ввод:
NumbersAndSquares(2) -
Ввод:
NumbersAndSquares(4)
Сумма от 1 до n
SumUpTo(n int) int, которая возвращает сумму всех чисел от 1 до n (1 + 2 + 3 + ... + n).SumUpTo(3) → 6SumUpTo(5) → 15-
Ввод:
SumUpTo(3) -
Ввод:
SumUpTo(5) -
Ввод:
SumUpTo(1) -
Ввод:
SumUpTo(0) -
Ввод:
SumUpTo(10)
Урок 4: Срезы
Что такое срез?
Срез (slice) — это последовательный набор элементов одного типа. В отличие от массивов, срезы имеют динамическую длину: в них можно добавлять новые элементы и удалять существующие.
Срезы очень похожи на массивы, но намного удобнее в реальной разработке. Если массив имеет фиксированный размер, то срез может увеличиваться по мере необходимости.
Поэтому в Go чаще всего работают именно со срезами, а не с обычными массивами.
Срезы — это как казахский той: можно запланировать мероприятие на 200 человек, а сколько гостей будет в итоге — неизвестно. Может 150? 250? Или все 300? Срезы обладают теми же качествами, что и организаторы тоя — гибкостью.
Мейрам "Пунктуальность" Тойбастаров — тот, кто "никогда" не опаздывает и смыслит в срезах
Главное отличие среза от массива — у среза длина может изменяться во время работы программы.
Массив vs срез
Визуально массив и срез очень похожи, но между ними есть важная разница.
Массив
Размер фиксированный. Если массив создан на 5 элементов, он всегда будет содержать ровно 5 элементов.
Срез
Размер динамический. В срез можно добавлять элементы и получать новый увеличенный срез.
В Go срез записывается почти как массив, но без указания размера внутри квадратных скобок.
Создание среза
Срезы создаются почти как массивы, только при создании не указывается длина.
package main
import "fmt"
func main() {
boxers := []string{"Muhammad Ali", "Mike Tyson", "George Foreman", "Joe Frazier"}
for _, value := range boxers {
fmt.Println(value)
}
}
Muhammad Ali
Mike Tyson
George Foreman
Joe FrazierОбратите внимание: в [] мы не указали длину — именно поэтому это slice.
Если написать [4]string, это будет массив. Если написать []string, это будет срез.
Срез разных типов
Срез может хранить элементы разных типов данных, но внутри одного среза все элементы должны быть одного типа.
Срез чисел:
numbers := []int{10, 20, 30, 40}
Срез строк:
cities := []string{"Almaty", "Astana", "Paris"}
Срез логических значений:
answers := []bool{true, false, true}
Создание среза через make()
Также срезы можно создавать при помощи функции make():
package main
import "fmt"
func main() {
cities := make([]string, 4)
cities[0] = "Almaty"
cities[1] = "Astana"
cities[2] = "New-York"
cities[3] = "Washington"
for _, value := range cities {
fmt.Println(value)
}
}
Almaty
Astana
New-York
WashingtonКогда мы пишем make([]string, 4), Go создаёт срез строк длиной 4. После этого мы можем обращаться к элементам по индексам от 0 до 3.
Теперь давайте посмотрим, как добавлять, удалять элементы, и что такое оператор среза.
Длина и вместимость: len и cap
У среза есть две важные характеристики: длина и вместимость.
len— сколько элементов сейчас находится в срезе;cap— сколько элементов может поместиться в текущем внутреннем массиве.
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30}
fmt.Println(len(numbers))
fmt.Println(cap(numbers))
}
3
3В начале len и cap часто равны. Но после добавления элементов через append вместимость может измениться.
Добавление элементов (append)
Для добавления элемента в срез используется функция append(slice, value).
package main
import "fmt"
func main() {
cities := []string{"Almaty", "Astana"}
cities = append(cities, "Paris")
cities = append(cities, "Marcel")
for _, value := range cities {
fmt.Println(value)
}
}
Almaty
Astana
Paris
MarcelВажно сохранить результат обратно в переменную: cities = append(cities, "Paris"). Функция append возвращает новый срез.
Добавление нескольких элементов
Через append можно добавить сразу несколько элементов.
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3}
numbers = append(numbers, 4, 5, 6)
fmt.Println(numbers)
}
[1 2 3 4 5 6]Добавление одного среза в другой
Иногда нужно добавить не одно значение, а все элементы другого среза. Для этого используется ....
package main
import "fmt"
func main() {
first := []int{1, 2, 3}
second := []int{4, 5, 6}
first = append(first, second...)
fmt.Println(first)
}
[1 2 3 4 5 6]Три точки ... раскрывают второй срез как набор отдельных элементов.
Оператор среза (s[i:j])
Оператор среза выглядит так: s[i:j], где:
s— исходный срезi— начальный индекс (включительно)j— конечный индекс (не включительно)
| Индекс | Значение |
|---|---|
| 0 | Almaty |
| 1 | Astana |
| 2 | New-York |
| 3 | Washington |
Например, если написать cities[0:2], мы получим элементы с индексами 0 и 1. Элемент с индексом 2 уже не входит в результат.
package main
import "fmt"
func main() {
cities := []string{"Almaty", "Astana", "New-York", "Washington", "Paris", "Marcel"}
kzCities := cities[0:2]
usaCities := cities[2:4]
frCities := cities[4:6]
fmt.Println(kzCities) // Almaty, Astana
fmt.Println(usaCities) // New-York, Washington
fmt.Println(frCities) // Paris, Marcel
}
[Almaty Astana]
[New-York Washington]
[Paris Marcel]Короткие формы среза
В Go можно не указывать начальный или конечный индекс.
s[:3]— взять элементы от начала до индекса 3, не включая 3;s[2:]— взять элементы от индекса 2 до конца;s[:]— взять весь срез.
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30, 40, 50}
fmt.Println(numbers[:3])
fmt.Println(numbers[2:])
fmt.Println(numbers[:])
}
[10 20 30]
[30 40 50]
[10 20 30 40 50]Изменение элемента среза
Как и в массивах, элементы среза можно менять по индексу.
package main
import "fmt"
func main() {
cities := []string{"Almaty", "Astana", "Paris"}
cities[2] = "London"
fmt.Println(cities)
}
[Almaty Astana London]Здесь элемент с индексом 2 был заменён с Paris на London.
Удаление элемента
Удаление обычно делают комбинацией append и оператора среза. Мы берём элементы до индекса и элементы после индекса, затем "склеиваем" их.
package main
import "fmt"
func main() {
capitals := []string{"Astana", "Washington", "Marcel", "Paris"}
num := 2 // удаляем элемент с индексом 2 (Marcel)
capitals = append(capitals[:num], capitals[num+1:]...)
fmt.Println(capitals)
}
[Astana Washington Paris]Важно: capitals[:num] — это элементы до num, а capitals[num+1:] — элементы после num. Три точки ... "раскрывают" срез как набор аргументов.
Как это работает:
capitals[:2]даёт[Astana Washington];capitals[3:]даёт[Paris];appendсоединяет эти части;- элемент
Marcelпропадает из результата.
Перебор среза через range
Срезы удобно перебирать через range. Мы можем получить и индекс, и значение.
package main
import "fmt"
func main() {
cities := []string{"Almaty", "Astana", "Paris"}
for index, value := range cities {
fmt.Println(index, value)
}
}
0 Almaty
1 Astana
2 ParisЕсли индекс не нужен
Если нужен только элемент, индекс можно заменить на _.
package main
import "fmt"
func main() {
cities := []string{"Almaty", "Astana", "Paris"}
for _, value := range cities {
fmt.Println(value)
}
}
Almaty
Astana
ParisПример: сумма элементов среза
Срезы часто используются вместе с циклами. Например, можно посчитать сумму всех чисел в срезе.
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30, 40}
sum := 0
for _, value := range numbers {
sum = sum + value
}
fmt.Println(sum)
}
100Пример: поиск максимального числа
С помощью цикла и среза можно найти максимальное число.
package main
import "fmt"
func main() {
numbers := []int{15, 90, 12, 45, 7}
max := numbers[0]
for _, value := range numbers {
if value > max {
max = value
}
}
fmt.Println(max)
}
90Частые ошибки при работе со срезами
- забыть сохранить результат
appendобратно в переменную; - обратиться к индексу, которого нет в срезе;
- перепутать массив
[5]intи срез[]int; - забыть, что конечный индекс в
s[i:j]не включается; - забыть поставить
..., когда добавляем один срез в другой.
Мини-практика
Чтобы лучше закрепить срезы, попробуйте написать программы для следующих задач:
- создать срез из 5 чисел и вывести все элементы;
- добавить в срез ещё несколько чисел через
append; - вывести только первые 3 элемента среза;
- вывести элементы с индекса 2 до конца;
- удалить элемент по индексу;
- посчитать сумму всех элементов;
- найти максимальное число;
- вывести только положительные числа.
Итоги
- срез похож на массив, но имеет динамическую длину;
- срез создаётся через
[]типбез указания размера; makeпозволяет создать срез нужной длины;appendдобавляет новые элементы;s[i:j]позволяет получить часть среза;- конечный индекс в
s[i:j]не включается; - элементы можно изменять по индексу;
- удаление можно сделать через
appendи оператор среза; rangeудобно использовать для перебора среза.
4-ый элемент
FourthElement(numbers []int) int, которая возвращает 4-ый элемент среза (индекс 3).FourthElement([]int{41, -231, 24, 49, 6}) → 49FourthElement([]int{1, 2, 3, 4}) → 4-
Ввод:
FourthElement([]int{41, -231, 24, 49, 6}) -
Ввод:
FourthElement([]int{1, 2, 3, 4}) -
Ввод:
FourthElement([]int{10, 20, 30, 40, 50, 60}) -
Ввод:
FourthElement([]int{-5, -10, -15, -20}) -
Ввод:
FourthElement([]int{100, 200, 300, 400, 500})
Минимум и максимум
MinMax(numbers []int) string, которая возвращает минимальное и максимальное значения среза через пробел, как показано в примерах.MinMax([]int{4, 9, 2, 5}) → 2 9MinMax([]int{1, 2, -1, 4, 5, 56, 7, 8, 9, -21}) → -21 56-
Ввод:
MinMax([]int{4, 9, 2, 5}) -
Ввод:
MinMax([]int{1, 2, -1, 4, 5, 56, 7, 8, 9, -21}) -
Ввод:
MinMax([]int{5}) -
Ввод:
MinMax([]int{-1, -2, -3}) -
Ввод:
MinMax([]int{100, 50, 75, 25, 0})
Разность максимума и минимума
MaxMinDiff(numbers []int) int, которая возвращает разность между максимальным и минимальным элементами среза.MaxMinDiff([]int{4, 9, 2, 5, 8, 7, 11, 16}) → 14MaxMinDiff([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) → 9-
Ввод:
MaxMinDiff([]int{4, 9, 2, 5, 8, 7, 11, 16}) -
Ввод:
MaxMinDiff([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) -
Ввод:
MaxMinDiff([]int{5}) -
Ввод:
MaxMinDiff([]int{-10, -5, -1}) -
Ввод:
MaxMinDiff([]int{100, 1})
Обратный порядок среза
Reverse(numbers []int) string, которая возвращает все элементы среза в обратном порядке через пробел.Reverse([]int{6, 19, 26, -3, 46, 8, 5, -65, 90, 25}) → 25 90 -65 5 8 46 -3 26 19 6Reverse([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) → 10 9 8 7 6 5 4 3 2 1-
Ввод:
Reverse([]int{6, 19, 26, -3, 46, 8, 5, -65, 90, 25}) -
Ввод:
Reverse([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) -
Ввод:
Reverse([]int{1}) -
Ввод:
Reverse([]int{1, 2}) -
Ввод:
Reverse([]int{5, 10, 15, 20, 25})
Только чётные элементы
EvensOnly(numbers []int) string, которая создаёт второй срез из чётных элементов первого среза и возвращает его элементы через пробел.EvensOnly([]int{4, 9, 2, 5, 8, 7, 11, 16}) → 4 2 8 16EvensOnly([]int{6, 19, 26, 3, 46, 8, 5, 65, 90, 25}) → 6 26 46 8 90-
Ввод:
EvensOnly([]int{4, 9, 2, 5, 8, 7, 11, 16}) -
Ввод:
EvensOnly([]int{6, 19, 26, 3, 46, 8, 5, 65, 90, 25}) -
Ввод:
EvensOnly([]int{1, 3, 5, 7}) -
Ввод:
EvensOnly([]int{2, 4, 6, 8}) -
Ввод:
EvensOnly([]int{0, -2, -4, 3})