Глава 3. Условия, логические выражения и остаток от деления


Как программа принимает решения

В этой главе вы научитесь выбирать действия с помощью if и else, составлять логические выражения и использовать остаток от деления для проверки чётности и работы с цифрами числа.

Как хранить решения нескольких задач

Решения удобно организовать в Visual Studio так, чтобы к каждому можно было вернуться. Возможны три варианта:

  1. Несколько методов в одном файле. Из Main вызывайте метод нужной задачи.
  2. Несколько классов в одном проекте. Дайте каждому классу понятное имя и вызывайте нужный метод из Main.
  3. Несколько проектов в одном решении (Solution). Перед запуском выберите нужный запускаемый проект.

Пока вы работаете с одной задачей, достаточно одного проекта и одного Main. Не вставляйте несколько методов Main с одинаковой сигнатурой в один класс.

Дополнительно: видео об организации решений на иврите

В необязательном видео показаны три способа организации задач:

Условный оператор if

Условный оператор выполняет действие только тогда, когда заданное условие истинно.

1
2
3
4
5
int score = 85;
if (score >= 60) // Выражение в скобках — логическое выражение
{
    Console.WriteLine("Экзамен сдан!");
}

Выражение score >= 60 проверяет, не меньше ли оценка 60. Если результат true («истина»), выполняется блок в фигурных скобках. Если результат false («ложь»), этот блок пропускается.

Первая таблица трассировки с условием

Когда в коде есть условие, добавьте в таблицу трассировки столбец с результатом логического выражения: true или false. Пронумеруйте строки кода и записывайте номер выполненной строки в первом столбце. Если значение переменной не изменилось, её ячейку можно оставить пустой.

Проследим выполнение при начальном значении score = 72:

1
2
3
4
5
6
7
8
9
int score = 72;
bool passed = false;

if (score >= 60)
{
    passed = true;
}

Console.WriteLine(passed);
Строка score passed score >= 60 Вывод
1 72      
2   false    
4     true  
6   true    
9       True

Строки 5 и 7 содержат только фигурные скобки: отдельные строки таблицы для них обычно не нужны. Значение bool в исходном коде записывается как true, а Console.WriteLine выводит его как True.

Попробуйте сами

Напишите программу, которая получает две оценки ученика по программированию: за первое и второе полугодие. Если вторая оценка выше первой не менее чем на 5 баллов, выведите сообщение «Есть прогресс!».

Проверьте случаи, когда разница равна 4, 5 и 6 баллам, а также случай, когда вторая оценка ниже первой.

Две ветви: if и else

else задаёт действие на случай, когда условие ложно. Из двух ветвей выполняется ровно одна.

1
2
3
4
5
6
7
8
9
int score = 55;
if (score >= 60) // Здесь условие ложно
{
    Console.WriteLine("Экзамен сдан!");
}
else // Поэтому выполнится строка 8, а строка 4 будет пропущена
{
    Console.WriteLine("Экзамен не сдан. Попробуйте ещё раз.");
}

Несколько вариантов: else if и else

1
2
3
4
5
6
7
8
9
10
string color = "Green";
if (color == "Red")
    Console.WriteLine("Красный цвет");
else if (color == "Green")
{
    Console.ForegroundColor = ConsoleColor.Green;
    Console.WriteLine("Зелёный цвет");
}
else
    Console.WriteLine($"Цвет: {color}");

Условия проверяются сверху вниз. После первой подходящей ветви остальные пропускаются. else выполняется, если ни одно из предыдущих условий не подошло.

Пример с решением: возрастная группа

Напишите программу, которая получает возраст и выводит:

  • «Несовершеннолетний», если возраст меньше 18;
  • «Взрослый», если возраст от 18 до 64 включительно;
  • «Пожилой», если возраст не меньше 65.
Решение
1
2
3
4
5
6
7
8
9
10
11
12
public static void Main()
{
    Console.Write("Введите возраст: ");
    int age = int.Parse(Console.ReadLine());

    if (age < 18)
        Console.WriteLine("Несовершеннолетний");
    else if (age <= 64)
        Console.WriteLine("Взрослый");
    else
        Console.WriteLine("Пожилой");
}

В ветви else if не нужно повторять проверку age >= 18: если мы дошли до неё, первое условие уже оказалось ложным.

Операторы сравнения и логические операторы

В математике В C# Вид Значение Пример
\(=\) == Сравнение Равно if (x == y) { … }
\(\neq\) != Сравнение Не равно if (x != y) { … }
\(>\) > Сравнение Больше if (x > y) { … }
\(<\) < Сравнение Меньше if (x < y) { … }
\(\ge\) >= Сравнение Больше или равно if (x >= y) { … }
\(\le\) <= Сравнение Меньше или равно if (x <= y) { … }
\(\land\) && Логический И if (a > 0 && b > 0) { … }
\(\lor\) || Логический ИЛИ if (a == 0 || b == 0) { … }
\(\lnot\) ! Логический НЕ bool ok = !isReady;

Логические выражения и тип bool

Логическое выражение (logical expression) имеет значение true или false. Например, age >= 18 сравнивает возраст с числом 18.

Переменная типа bool хранит одно из этих двух значений. В неё можно записать результат сравнения:

Console.WriteLine("Введите два числа, каждое на отдельной строке:");
int num1 = int.Parse(Console.ReadLine());
int num2 = int.Parse(Console.ReadLine());
bool found = (num1 == num2);

if (found)
    Console.WriteLine("Числа равны!");
else
    Console.WriteLine("Числа не равны.");

Ещё один пример:

int age = 18;
bool isAdult = age >= 18; // Результат сравнения true записывается в isAdult

В строке выше = выполняет присваивание, а >= — сравнение. Сначала вычисляется выражение справа, затем его результат сохраняется в переменной.

Примеры выражений; здесь предполагается, что переменные уже объявлены:

// Простое сравнение: истинно, если x положительно
x > 0

// Логическое И: истинно, если x положительно и чётно
(x > 0) && (x % 2 == 0)

// a не равно b, и при этом c больше d или e не больше f
(a != b) && ((c > d) || (e <= f))

Это примеры выражений, а не готовые инструкции программы: используйте их внутри if или справа от = при присваивании переменной типа bool.

Остаток от деления: оператор %

Оператор % возвращает остаток от деления. Например, 10 % 3 равно 1, потому что \(10 = 3 \cdot 3 + 1\). Он помогает проверять чётность, делимость и выделять цифры числа.

В C# знак % не вычисляет проценты. Например, 20 % 6 даёт остаток 2, а не шесть процентов от двадцати.

int number = 7;
if (number % 2 == 0)
{
    Console.WriteLine("Чётное число");
}
else
{
    Console.WriteLine("Нечётное число");
}

При number = 7 программа выведет:

Нечётное число

Составные условия

С помощью логических операторов можно объединять проверки:

  • &&И: обе проверки должны дать true;
  • ||ИЛИ: хотя бы одна проверка должна дать true;
  • !НЕ: меняет true на false и наоборот.

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

1
2
3
4
5
6
7
8
9
10
11
int age = 25;
bool hasLicense = true;

if (age >= 18 && hasLicense)
{
    Console.WriteLine("Условия для вождения выполнены");
}
else
{
    Console.WriteLine("Условия для вождения не выполнены");
}

hasLicense уже имеет тип bool, поэтому можно написать && hasLicense без дополнительного сравнения с true.

Переменные-флаги

Флаг — логическая переменная, которая запоминает состояние. Например, found хранит, найдено ли нужное число:

1
2
3
4
5
6
7
8
9
10
11
12
bool found = false;
int number = 5;

if (number == 5)
{
    found = true;
}

if (found)
{
    Console.WriteLine("Число найдено");
}

Фигурные скобки и оформление кода

В предыдущих примерах открывающая фигурная скобка стоит на новой строке. Этот стиль называется Allman; используйте его для единообразия с примерами курса.

В других материалах вы можете встретить стиль K&R, где открывающая скобка стоит на той же строке, что и условие. Смысл кода не меняется:

bool found = false;
int number = 5;

if (number == 5) {
    found = true;
}

if (found) {
    Console.WriteLine("Число найдено");
}

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

bool found = false;
int number = 5;

if (number == 5)
    found = true;

if (found)
    Console.WriteLine("Число найдено");

Для проверки равенства используйте ==. Один знак = означает присваивание. Например, if (number = 5) не скомпилируется: условие должно иметь тип bool.

Первый if в этом примере можно заменить присваиванием результата сравнения:

1
2
3
4
5
6
7
bool found = false;
int number = 5;

found = number == 5; // В found записывается true

if (found)
    Console.WriteLine("Число найдено");

Палиндром: решение и таблица трассировки

Напишите программу, которая получает положительное трёхзначное целое число num и сообщает, является ли оно палиндромом. Палиндром одинаково читается слева направо и справа налево. Например, 363 — палиндром, а 563 — нет.

Решение и трассировка
1
2
3
4
5
6
7
8
9
10
11
12
Console.Write("Введите положительное трёхзначное число: ");
int num = int.Parse(Console.ReadLine());
int units = num % 10; // Последняя цифра
int hundreds = num / 100; // Первая цифра; деление целочисленное
if (units == hundreds)
{
    Console.WriteLine($"{num} — палиндром");
}
else
{
    Console.WriteLine($"{num} — не палиндром");
}

Таблица трассировки для ввода 363:

Строка num units hundreds units == hundreds Вывод
1         Введите положительное трёхзначное число:
2 363        
3 363 3      
4 363 3 3    
5 363 3 3 true  
7 363 3 3   363 — палиндром

Таблица помогает проследить выполнение на бумаге и объяснить назначение программы. Проверка выполняется в строке 5, а сообщение выводится отдельной инструкцией в строке 7.

Для ввода 563 запишем только изменившиеся значения:

Строка num units hundreds units == hundreds Вывод
1         Введите положительное трёхзначное число:
2 563        
3   3      
4     5    
5       false  
11         563 — не палиндром

Частые ошибки и проверка понимания

Лишняя точка с запятой после if

В следующем коде сообщение «Успех» выводится всегда:

if (num > 5);
    Console.WriteLine("Успех");

Точка с запятой сразу после условия образует пустую инструкцию. Последующий вывод уже не относится к if. Код может скомпилироваться, поэтому проверяйте разные значения, обращайте внимание на предупреждения Visual Studio и используйте автоматическое форматирование.

Отдельные if и цепочка else if

В конструкции if/else выполняется ровно одна из двух ветвей. Если написать несколько независимых if подряд, могут выполниться несколько ветвей или ни одной. В цепочке if/else if выполняется не более одной ветви; завершающий else обеспечивает вариант на случай, когда все условия ложны.

У каждой проверки должны быть обе части

В C# нельзя записать диапазон как if (3 < x < 5). Правильно:

if (x > 3 && x < 5)
    Console.WriteLine("Число внутри диапазона");

Запись if (myResult < 5 || > 8) тоже неверна. Нужна полная проверка по обе стороны ||:

if (myResult < 5 || myResult > 8)
    Console.WriteLine("Число вне отрезка от 5 до 8");

Приоритет логических операторов

&& имеет более высокий приоритет, чем ||, подобно тому как умножение выполняется раньше сложения. Скобки могут изменить результат:

int a = 5, b = 6, c = 7, d = 8, e = 9, f = 10;
bool result1 = a == b && c > d || e <= f; // true
bool result2 = a == b && (c > d || e <= f); // false
bool result2b = (a == b) && ((c > d) || (e <= f)); // То же, что result2

Для result1 ложная левая часть объединяется через ИЛИ с истинным e <= f, поэтому результат — true. Для result2 первое сравнение a == b ложно, поэтому всё выражение с И даёт false.

Дополнительно: материалы на иврите

Эти материалы необязательны; основные объяснения и практика доступны на русском:

Следующие шаги

Практика 3.1: простые условия — задания и решения

Практика 3.2: составные условия и остаток от деления

Следующая глава: цикл for → · Содержание курса