4. Условия, циклы и граничные значения

Изучите if, for, while и диапазон повторяющихся утверждений.

Wave Foundation

Выберите путь выполнения

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

Сохраните каждый пример в main.wave и запустите его. При чтении кода запишите на листе бумаги текущие значения переменных, условия, которые будут проверяться дальше, и порядок выполнения операторов. Гораздо важнее тренироваться следовать потоку, чем запоминать результаты.

if и else

Условия пишутся в круглых скобках, а текст заключается в фигурные скобки. В следующем примере замените balance на 500 или 2000, чтобы определить, какая ветвь выполняется.

fun main() {
    var balance: i32 = 2000;
    var price: i32 = 1200;

    if (balance >= price) {
        balance -= price;
        println("bought, balance={}", balance);
    } else {
        println("not enough money");
    }
}

Результат выполнения:

bought, balance=800

Оба блока не выполняются. Если условие истинно, выполняется первый блок. Если условие ложно, выполняется блок else. Покупки разрешены, даже если balance равен price, поэтому я написал >=. Если вы измените его на >, операция изменится на ту же сумму.

Порядок нескольких условий

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

fun grade(score: i32) {
    if (score < 0 || score > 100) {
        println("invalid");
    } else if (score >= 90) {
        println("A");
    } else if (score >= 80) {
        println("B");
    } else {
        println("C");
    }
}

fun main() {
    grade(95);
    grade(80);
    grade(79);
    grade(101);
}

Результат выполнения:

A
B
C
invalid

Недействительные оценки сначала отклоняются, а затем оцениваются. Если вы поставите score >= 80 в начале, вы никогда не дойдете до ветки A, потому что в эту ветку входит 95 градусов. Проверьте порядок условий, а также правильность каждого условия.

Не меняйте значения в условных выражениях

Операции присваивания, составного присваивания, а также увеличения или уменьшения не допускаются в условиях if, while или for. Используйте == для сравнения. Чтобы обновить значение и затем проверить его, напишите два отдельных оператора.

Правильная форма фрагмента внутри функции:

value = read_value();

if (value == expected) {
    println("matched");
}

read_value и expected не определены в этом фрагменте, поэтому это не полная программа, которая работает как есть. Здесь показано правило: «Сравнить после изменения состояния».

while: Пока условия сохраняются

fun main() {
    var remaining: i32 = 3;

    while (remaining > 0) {
        println("{}", remaining);
        remaining -= 1;
    }

    println("finished at {}", remaining);
}

Результат выполнения:

3
2
1
finished at 0

Условия проверяются перед входом в кузов. Если remaining с самого начала равен 0, тело никогда не выполняется. Если декремент в конце тела опущен, условие остается истинным и цикл не завершается.

После написания цикла проверьте «Что приближает его к условию завершения?» Если это цикл, ожидающий ввода, его роль играет изменение ввода или EOF, а если это числовой цикл, свою роль играет обновление индекса.

for: Инициализация/Условия/Обновление

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

fun main() {
    var total: i32 = 0;

    for (var number: i32 = 1; number <= 5; number += 1) {
        total += number;
    }

    println("sum={}", total);
}

Результат выполнения:

sum=15
  1. Инициализируйте number значением 1. Этот шаг выполняется один раз.
  2. Проверяет number <= 5. Если ложно, завершить итерацию.
  3. Добавьте в текст от number до total.
  4. Увеличьте number на 1 и вернитесь к проверке состояния.

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

Границы включения и исключения

Натуральная сумма от 1 до 5 равна <= 5. С другой стороны, индекс массива длиной 5 должен использовать < 5. Это связано с тем, что индексы массива начинаются с 0 и заканчиваются 4.

Не путайте «выполнить пять раз» с «до значения 5 включительно». Определить количество повторений можно, посмотрев одновременно начальное и конечное значения. Случаи, когда входные данные пусты и содержат только один элемент, хороши для обнаружения ошибок границ.

continue и break

continue пропускает оставшуюся часть этой итерации, а break завершает ближайшую итерацию.

fun main() {
    var total: i32 = 0;

    for (var number: i32 = 1; number <= 10; number += 1) {
        if (number == 3) {
            continue;
        }

        if (number == 6) {
            break;
        }

        total += number;
    }

    println("{}", total);
}

Результат выполнения:

12

Общее число: 1, 2, 4 и 5. Если continue встречается в for, процесс обновления продолжится. Поскольку while не имеет отдельного выражения обновления, такого как for, вы должны быть осторожны, чтобы не пропустить какие-либо изменения состояния, необходимые до continue.

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

Разделите падеж на match

Вы можете использовать match при сравнении нескольких вариантов одного и того же значения. Тело каждого arm представляет собой блок.

fun describe(status: i32) {
    match (status) {
        200 => {
            println("ok");
        }
        404 => {
            println("missing");
        }
        _ => {
            println("other");
        }
    }
}

fun main() {
    describe(200);
    describe(404);
    describe(500);
}

Результат выполнения:

ok
missing
other

_ — это шаблон, который обрабатывает все остальное. Не размещайте дубликаты внутри одного и того же match. variant, который имеет разные типы данных в зависимости от значения, описан в Глава модели данных.

Полный пример: подсчет чисел, соответствующих условиям

Найдите количество и сумму четных чисел от 1 до 10. Поскольку количество и сумма — это разная информация, они накапливаются как переменные.

fun main() {
    var count: i32 = 0;
    var total: i32 = 0;

    for (var number: i32 = 1; number <= 10; number += 1) {
        if (number % 2 == 0) {
            count += 1;
            total += number;
        }
    }

    println("count={} total={}", count, total);
}

Результат выполнения:

count=5 total=30

Четные числа — 2, 4, 6, 8 и 10, поэтому число — 5, а сумма — 30. Даже если выражение короткое, границу итерации легко проверить, если сначала проверить результат в небольшом диапазоне, который можно получить вручную.

Упражнение и полное решение

Добавляйте только числа, кратные 3, от 1 до 20, но не добавляйте значения, сумма которых превышает 30. Нам нужно различать «добавление, а затем проверка, закончилось ли оно» и «проверка, закончилось ли оно, а затем добавление».

fun main() {
    var total: i32 = 0;

    for (var number: i32 = 1; number <= 20; number += 1) {
        if (number % 3 != 0) {
            continue;
        }

        if (total + number > 30) {
            break;
        }

        total += number;
    }

    println("{}", total);
}

Результат выполнения:

30

Значения 3+6+9+12 в сумме дают 30, поэтому следующее значение, 15, не добавляется. Эти небольшие входные данные безопасны, но для больших целых чисел проверка total + number может сама переполниться. Написание проверки не обрабатывает автоматически каждый граничный случай.