Операторы и выражения
Учимся складывать, сравнивать и строить логику — три кита любой программы
В предыдущем уроке мы научились общаться с пользователем: выводить текст на экран и считывать то, что он вводит с клавиатуры. Но программа, которая только читает и печатает, не очень полезна. Настоящая сила программирования начинается тогда, когда мы начинаем вычислять, сравнивать и принимать решения. Именно для этого нужны операторы. Оператор — это символ или комбинация символов, которые говорят компьютеру: «Возьми эти данные и сделай с ними что-то». Сложи два числа, проверь, равны ли они, определи, выполняются ли оба условия одновременно. Сегодня мы разберём три большие группы операторов: арифметические, сравнения и логические. А в конце урока напишем полноценный калькулятор с выбором операции.
🔢 Арифметические операторы
Начнём с самого знакомого — математики. Арифметические операторы работают точно так же, как в школьной тетради, только записываются символами на клавиатуре. В Go их пять:
+ — сложение
- — вычитание
* — умножение
/ — деление
% — остаток от деления (модуль)
Давайте посмотрим, как это выглядит в коде:
package main
import "fmt"
func main() {
a := 10
b := 3
fmt.Println("a + b =", a+b) // 13
fmt.Println("a - b =", a-b) // 7
fmt.Println("a * b =", a*b) // 30
fmt.Println("a / b =", a/b) // 3
fmt.Println("a % b =", a%b) // 1
}
Всё очевидно, кроме двух моментов. Первый — деление. Мы разделили 10 на 3 и получили 3, а не 3.333... Почему? Потому что обе переменные — целые числа (int). Когда Go делит целое на целое, он отбрасывает дробную часть. Это называется целочисленное деление.
Если вам нужен точный результат с дробной частью, используйте float64:
package main
import "fmt"
func main() {
a := 10.0
b := 3.0
fmt.Println("a / b =", a/b) // 3.3333333333333335
}
Второй момент — оператор % (остаток от деления). Он показывает, сколько «осталось» после деления. Например, 10 делим на 3: получается 3 целых и 1 в остатке. Значит, 10 % 3 = 1. Ещё примеры:
fmt.Println(15 % 5) // 0 — 15 делится на 5 без остатка
fmt.Println(17 % 5) // 2 — 17 = 5*3 + 2
fmt.Println(7 % 2) // 1 — 7 = 2*3 + 1
fmt.Println(8 % 2) // 0 — 8 делится на 2 без остатка
💡 Подсказка: оператор % невероятно полезен на практике. Хотите проверить, чётное ли число? Просто посмотрите: n % 2 == 0. Если остаток от деления на 2 равен нулю — число чётное. Этот приём используется постоянно.
Помимо базовых операторов, в Go есть составные операторы присваивания. Они позволяют записать изменение переменной короче:
package main
import "fmt"
func main() {
x := 10
x += 5 // то же самое, что x = x + 5 → x теперь 15
fmt.Println(x)
x -= 3 // то же самое, что x = x - 3 → x теперь 12
fmt.Println(x)
x *= 2 // то же самое, что x = x * 2 → x теперь 24
fmt.Println(x)
x /= 4 // то же самое, что x = x / 4 → x теперь 6
fmt.Println(x)
x %= 4 // то же самое, что x = x % 4 → x теперь 2
fmt.Println(x)
}
Запись x += 5 и x = x + 5 делают абсолютно одно и то же. Составные операторы — просто более короткая и привычная для программистов форма записи. Используйте то, что вам понятнее.
📐 Приоритет операций
Как и в обычной математике, в Go операции выполняются не слева направо, а по приоритету. Умножение и деление выполняются раньше сложения и вычитания:
package main
import "fmt"
func main() {
result := 2 + 3*4
fmt.Println(result) // 14, а не 20
}
Сначала Go вычислил 3 * 4 = 12, а потом прибавил 2. Если вы хотите изменить порядок, используйте скобки, точно как в школе:
result := (2 + 3) * 4
fmt.Println(result) // 20
💡 Подсказка: если вы сомневаетесь в порядке операций — ставьте скобки. Даже если они не обязательны, скобки делают код понятнее. Лучше написать (a * b) + c, чем заставлять читателя вспоминать приоритеты.
⚖️ Операторы сравнения
Арифметические операторы дают нам числа. Операторы сравнения дают нам ответ на вопрос: правда или ложь? Результат сравнения — это всегда bool: либо true (правда), либо false (ложь).
== — равно
!= — не равно
< — меньше
> — больше
<= — меньше или равно
>= — больше или равно
Посмотрим на примере:
package main
import "fmt"
func main() {
a := 10
b := 20
fmt.Println("a == b:", a == b) // false — 10 не равно 20
fmt.Println("a != b:", a != b) // true — 10 действительно не равно 20
fmt.Println("a < b:", a < b) // true — 10 меньше 20
fmt.Println("a > b:", a > b) // false — 10 не больше 20
fmt.Println("a <= b:", a <= b) // true — 10 меньше или равно 20
fmt.Println("a >= b:", a >= b) // false — 10 не больше и не равно 20
}
Обратите внимание на важнейшее различие: одно равно = — это присваивание (записать значение в переменную), а два равно == — это сравнение (проверить, равны ли значения). Это одна из самых частых ошибок у новичков:
x := 5 // присваивание: x теперь равно 5
x == 5 // сравнение: "x равно 5?" — вернёт true
Сравнивать можно не только числа. Строки тоже поддерживают операторы сравнения:
package main
import "fmt"
func main() {
name := "Go"
fmt.Println(name == "Go") // true
fmt.Println(name == "Python") // false
fmt.Println(name != "Java") // true
// Строки сравниваются по алфавиту (лексикографически)
fmt.Println("apple" < "banana") // true — "a" идёт раньше "b"
fmt.Println("go" < "Go") // false — маленькие буквы "больше" заглавных
}
Пока что результаты сравнений мы просто печатаем. Но уже в следующем уроке мы будем использовать их в условиях if, чтобы программа могла принимать решения: «если число больше нуля — сделай одно, иначе — другое».
🧠 Логические операторы
Логические операторы работают с bool-значениями (true и false). Они позволяют комбинировать несколько условий вместе. В Go их три:
&& — логическое И (AND). Результат true, только если оба условия истинны.
|| — логическое ИЛИ (OR). Результат true, если хотя бы одно условие истинно.
! — логическое НЕ (NOT). Переворачивает значение: true становится false, и наоборот.
Давайте разберём каждый с помощью жизненных аналогий.
Оператор && (И)
Представьте, что вы хотите поехать на пляж. Для этого нужно, чтобы и погода была хорошая, и у вас был выходной. Если хотя бы одно условие не выполняется — поездка срывается.
package main
import "fmt"
func main() {
goodWeather := true
dayOff := true
canGoToBeach := goodWeather && dayOff
fmt.Println("Едем на пляж?", canGoToBeach) // true
// Если погода плохая — не едем, даже если выходной
goodWeather = false
canGoToBeach = goodWeather && dayOff
fmt.Println("Едем на пляж?", canGoToBeach) // false
}
Оператор || (ИЛИ)
Вы голодны и думаете, где поесть. Вы будете сыты, если или приготовите дома, или закажете доставку. Достаточно одного из двух.
package main
import "fmt"
func main() {
cookAtHome := false
orderDelivery := true
willEat := cookAtHome || orderDelivery
fmt.Println("Буду сыт?", willEat) // true — доставку заказали
// Если ни то, ни другое...
cookAtHome = false
orderDelivery = false
willEat = cookAtHome || orderDelivery
fmt.Println("Буду сыт?", willEat) // false — голодаем
}
Оператор ! (НЕ)
Самый простой: он переворачивает значение на противоположное.
package main
import "fmt"
func main() {
isRaining := true
fmt.Println("Идёт дождь?", isRaining) // true
fmt.Println("Нет дождя?", !isRaining) // false
isLoggedIn := false
fmt.Println("Не авторизован?", !isLoggedIn) // true
}
Теперь самое интересное — комбинирование. В реальных программах логические операторы часто используются вместе с операторами сравнения:
package main
import "fmt"
func main() {
age := 25
hasLicense := true
// Можно ли водить? Нужно быть старше 18 И иметь права
canDrive := age >= 18 && hasLicense
fmt.Println("Может водить?", canDrive) // true
temperature := 36.6
// Температура нормальная? Между 36.0 и 37.0
isNormal := temperature >= 36.0 && temperature <= 37.0
fmt.Println("Температура в норме?", isNormal) // true
score := 85
// Отличник? Оценка от 90 и выше
// Хорошист? Оценка от 70 и выше, но ниже 90
isExcellent := score >= 90
isGood := score >= 70 && score < 90
fmt.Println("Отличник?", isExcellent) // false
fmt.Println("Хорошист?", isGood) // true
}
💡 Подсказка: приоритет логических операторов: сначала !, потом &&, потом ||. То есть a || b && c означает a || (b && c), а не (a || b) && c. Если сомневаетесь — ставьте скобки, это всегда безопаснее и понятнее.
📊 Таблицы истинности
Чтобы вы полностью поняли, как работают логические операторы, вот полные таблицы всех возможных комбинаций. Запоминать их наизусть не нужно — через пару дней практики вы начнёте «чувствовать» их интуитивно.
Давайте проверим это кодом:
package main
import "fmt"
func main() {
fmt.Println("=== Оператор && (И) ===")
fmt.Println("true && true =", true && true) // true
fmt.Println("true && false =", true && false) // false
fmt.Println("false && true =", false && true) // false
fmt.Println("false && false =", false && false) // false
fmt.Println()
fmt.Println("=== Оператор || (ИЛИ) ===")
fmt.Println("true || true =", true || true) // true
fmt.Println("true || false =", true || false) // true
fmt.Println("false || true =", false || true) // true
fmt.Println("false || false =", false || false) // false
fmt.Println()
fmt.Println("=== Оператор ! (НЕ) ===")
fmt.Println("!true =", !true) // false
fmt.Println("!false =", !false) // true
}
Запомните простое правило: && требует, чтобы все были true. || довольствуется тем, что хотя бы один true. Один «строгий», другой «лояльный».
🔗 Оператор + для строк
Интересный факт: оператор + в Go умеет не только складывать числа, но и склеивать строки. Это называется конкатенация:
package main
import "fmt"
func main() {
firstName := "Иван"
lastName := "Петров"
fullName := firstName + " " + lastName
fmt.Println(fullName) // Иван Петров
greeting := "Привет, " + fullName + "!"
fmt.Println(greeting) // Привет, Иван Петров!
}
Обратите внимание: мы складываем строку firstName, строку " " (пробел) и строку lastName. Результат — новая строка, в которой все части «склеены» вместе.
💡 Подсказка: нельзя складывать строку с числом: "Возраст: " + 25 вызовет ошибку. Для этого используйте fmt.Sprintf: fmt.Sprintf("Возраст: %d", 25). Мы подробно разобрали это в предыдущем уроке про форматирование.
🔄 Инкремент и декремент
В Go есть два специальных оператора для увеличения и уменьшения числа на единицу:
package main
import "fmt"
func main() {
counter := 0
counter++ // увеличиваем на 1 (то же, что counter = counter + 1)
fmt.Println(counter) // 1
counter++ // ещё раз на 1
fmt.Println(counter) // 2
counter-- // уменьшаем на 1 (то же, что counter = counter - 1)
fmt.Println(counter) // 1
}
Важный момент: в Go ++ и -- — это инструкции, а не выражения. Это значит, что нельзя написать y := x++ или fmt.Println(x++). Можно только x++ как отдельную строку. Go намеренно сделан так, чтобы код был простым и однозначным.
🛠 Практика: программа-калькулятор
Пришло время собрать всё вместе. Мы напишем программу-калькулятор, которая спрашивает у пользователя два числа и операцию, а затем выводит результат. Эта программа использует почти всё, что мы изучили: переменные, ввод/вывод и арифметические операторы.
Для начала создайте папку и файл для проекта:
mkdir calculator
cd calculator
go mod init calculator
Создайте файл main.go и напишите в нём:
package main
import "fmt"
func main() {
var a, b float64
var operator string
// Просим ввести первое число
fmt.Print("Введите первое число: ")
fmt.Scan(&a)
// Просим выбрать операцию
fmt.Print("Введите операцию (+, -, *, /): ")
fmt.Scan(&operator)
// Просим ввести второе число
fmt.Print("Введите второе число: ")
fmt.Scan(&b)
// Вычисляем результат
var result float64
var validOperation bool
validOperation = true
if operator == "+" {
result = a + b
} else if operator == "-" {
result = a - b
} else if operator == "*" {
result = a * b
} else if operator == "/" {
if b == 0 {
fmt.Println("Ошибка: деление на ноль невозможно!")
validOperation = false
} else {
result = a / b
}
} else {
fmt.Println("Неизвестная операция:", operator)
validOperation = false
}
// Выводим результат
if validOperation {
fmt.Printf("%.2f %s %.2f = %.2f\n", a, operator, b, result)
}
}
Запустите программу:
go run main.go
Пример работы:
Введите первое число: 15.5
Введите операцию (+, -, *, /): *
Введите второе число: 3
15.50 * 3.00 = 46.50
Разберём ключевые моменты программы:
1. Мы используем float64 для чисел, чтобы калькулятор мог работать с дробями. Если бы мы выбрали int, то 10 / 3 давало бы 3, а не 3.33.
2. Переменная operator имеет тип string — пользователь вводит символ операции как текст.
3. Мы проверяем деление на ноль: если пользователь попытается разделить на 0, программа выдаст понятную ошибку вместо некорректного результата.
4. Формат %.2f выводит число с двумя знаками после запятой. Мы изучили это в предыдущем уроке.
💡 Подсказка: в этой программе мы используем конструкцию if / else if / else, которую мы ещё не изучали формально. Пока просто прочитайте код — он интуитивно понятен: «если оператор это плюс — складываем, иначе если минус — вычитаем» и так далее. В следующем уроке мы разберём условия подробно.
А вот как можно расширить наш калькулятор, добавив оператор остатка от деления и возведение в квадрат:
package main
import "fmt"
func main() {
var a, b float64
var operator string
fmt.Println("===== КАЛЬКУЛЯТОР =====")
fmt.Println("Доступные операции: + - * / % sq")
fmt.Println("sq — возведение первого числа в квадрат")
fmt.Println("=======================")
fmt.Print("\nВведите первое число: ")
fmt.Scan(&a)
fmt.Print("Введите операцию: ")
fmt.Scan(&operator)
// Для операции "sq" второе число не нужно
if operator != "sq" {
fmt.Print("Введите второе число: ")
fmt.Scan(&b)
}
var result float64
valid := true
if operator == "+" {
result = a + b
} else if operator == "-" {
result = a - b
} else if operator == "*" {
result = a * b
} else if operator == "/" {
if b == 0 {
fmt.Println("Ошибка: деление на ноль!")
valid = false
} else {
result = a / b
}
} else if operator == "%" {
if b == 0 {
fmt.Println("Ошибка: деление на ноль!")
valid = false
} else {
result = float64(int(a) % int(b))
}
} else if operator == "sq" {
result = a * a
} else {
fmt.Println("Неизвестная операция:", operator)
valid = false
}
if valid {
if operator == "sq" {
fmt.Printf("%.2f в квадрате = %.2f\n", a, result)
} else {
fmt.Printf("%.2f %s %.2f = %.2f\n", a, operator, b, result)
}
}
}
Попробуйте запустить эту версию и поэкспериментировать. Введите разные числа, попробуйте деление на ноль, попробуйте несуществующую операцию. Видите, как программа обрабатывает каждый случай? Это и есть программирование — предусматривать все возможные ситуации.
⚠️ Типичные ошибки новичков
Разберём ошибки, с которыми вы наверняка столкнётесь:
Путаница между = и ==
В Go компилятор не даст вам использовать = вместо == в условиях — это просто не скомпилируется. Но путаница в голове всё равно бывает. Запомните: = — «записать», == — «сравнить».
Целочисленное деление
Если оба числа — int, результат деления тоже будет int (дробная часть отбрасывается). 7 / 2 = 3, а не 3.5. Если нужен точный результат, используйте float64.
Сложение разных типов
Go — строго типизированный язык. Нельзя сложить int и float64 напрямую. Нужно привести к одному типу: float64(myInt) + myFloat.
Деление на ноль
Деление целого числа на ноль вызовет панику (аварийное завершение программы). Для float64 деление на ноль даст +Inf (бесконечность). В обоих случаях это не то, чего хочет пользователь — поэтому всегда проверяйте делитель перед делением.
📋 Итоги урока
- Изучили арифметические операторы:
+,-,*,/,%и составные варианты+=,-=,*=,/=,%= - Разобрались с целочисленным делением:
int / intотбрасывает дробную часть - Узнали про операторы сравнения:
==,!=,<,>,<=,>=— они всегда возвращаютbool - Освоили логические операторы:
&&(оба истинны),||(хотя бы один истинен),!(отрицание) - Научились использовать
+для склеивания строк (конкатенация) - Познакомились с инкрементом
++и декрементом-- - Написали полноценную программу-калькулятор с проверкой деления на ноль
В следующем уроке мы разберём условия: if, else, else if и switch. Вы узнаете, как заставить программу принимать решения и выполнять разный код в зависимости от ситуации. Мы также познакомимся с крутой фишкой Go — коротким if с инициализацией и switch без break. Операторы сравнения и логики, которые мы сегодня изучили, станут основой для написания условий.