Skip to content

Управляющие конструкции

This content is not available in your language yet.

Самая «питоновская» глава раздела: почти всё переносится дословно. Поэтому сосредоточимся на трёх вещах, которые всё-таки отличаются, — и на одной, которой в Mojo пока нет вовсе.

def main():
var temperature = 25
if temperature > 30:
print("жарко")
elif temperature > 20:
print("тепло")
else:
print("прохладно")
print("одеваться:", "легко" if temperature > 20 else "тепло")
Результат

тепло одеваться: легко

if / elif / else и тернарный оператор — без единого отличия от Python.

Если вы привыкли к сопоставлению с образцом из Python 3.10, придётся обойтись без него:

error: unexpected token in expression
Что это значит

Конструкция match / case в Mojo 1.0 не поддерживается — компилятор не знает такого ключевого слова и спотыкается на нём как на обычном выражении.

Как исправить

Пишите цепочку if / elif / else. Для разбора по типам в Mojo есть более мощный инструмент — перегрузка функций, которая решает задачу на этапе компиляции.

while и for ... in работают привычно, range принимает те же три формы, включая отрицательный шаг:

def main():
for i in range(10, 0, -3):
print(i, end=" ")
print()
Результат

10 7 4 1

Есть и редкая питоновская конструкция for ... else — блок else выполняется, если цикл завершился без break. Работает она и с while.

enumerate и zip на месте:

def main():
var cities: List[String] = ["Москва", "Казань"]
var population: List[Int] = [13, 1]
for index, city in enumerate(cities):
print(index, city)
for pair in zip(cities, population):
print(pair[0], "—", pair[1], "млн")
Результат

0 Москва 1 Казань Москва — 13 млн Казань — 1 млн

Обратите внимание на разницу: enumerate даёт распаковку в две переменные, а элементы zip приходят кортежем, к которому обращаются по индексу.

Есть и reversed для обхода в обратном порядке.

А вот это отличается от Python по существу. В Python переменная цикла — копия ссылки на элемент, и присваивание ей ничего не меняет в списке. В Mojo можно получить ссылку на элемент через ref:

🐍 Python
for i in range(len(numbers)):
numbers[i] *= 10
🔥 Mojo
for ref value in numbers:
value *= 10

Вариант с индексами тоже работает, но ref читается лучше и не даёт ошибиться с границами. Без ref переменная цикла — копия, и умножение ничего не изменит в списке.

Здесь Mojo честнее Python. Обращение по ключу может не найти значение, поэтому оно способно бросить исключение:

error: cannot call function that may raise in a context that cannot raise
Что это значит

Вы написали codes[city] внутри обычной функции. Обращение к словарю по ключу — операция, которая может завершиться ошибкой, если ключа нет.

Как исправить

Либо пометьте функцию как raises, либо перебирайте пары через .items() — тогда ключ и значение приходят сразу, и обращаться по ключу не нужно.

def main():
var codes = Dict[String, Int]()
codes["Москва"] = 495
codes["Казань"] = 843
for item in codes.items():
print(item.key, item.value)
Результат

Москва 495 Казань 843

Отдельно доступны .keys() и .values(), а for city in codes перебирает ключи — как в Python.

loops.mojo
def main():
# Ветвление и тернарный оператор
var temperature = 25
if temperature > 30:
print("жарко")
elif temperature > 20:
print("тепло")
else:
print("прохладно")
print("одеваться:", "легко" if temperature > 20 else "тепло")
# range: с началом, концом и шагом, в том числе отрицательным
for i in range(10, 0, -3):
print(i, end=" ")
print()
# Перебор списка вместе с индексом
var cities: List[String] = ["Москва", "Казань", "Томск"]
for index, city in enumerate(cities):
print(index, city)
# Два списка параллельно
var population: List[Int] = [13, 1, 0]
for pair in zip(cities, population):
print(pair[0], "—", pair[1], "млн")
# Изменение элементов прямо в цикле: ref даёт ссылку на элемент
var numbers: List[Int] = [1, 2, 3]
for ref value in numbers:
value *= 10
print("после умножения:", numbers[0], numbers[1], numbers[2])
# break и continue
var sum = 0
for value in range(1, 10):
if value % 2 == 0:
continue
if value > 7:
break
sum += value
print("сумма нечётных до 7:", sum)
# У циклов есть else — он срабатывает, если не было break
for value in range(2):
print("шаг", value)
else:
print("цикл дошёл до конца")
# Перебор словаря: items() даёт сразу ключ и значение
var codes = Dict[String, Int]()
codes["Москва"] = 495
codes["Казань"] = 843
for item in codes.items():
print(item.key, item.value)
Результат

тепло одеваться: легко 10 7 4 1 0 Москва 1 Казань 2 Томск Москва — 13 млн Казань — 1 млн Томск — 0 млн после умножения: 10 20 30 сумма нечётных до 7: 16 шаг 0 шаг 1 цикл дошёл до конца Москва 495 Казань 843

Проверено на Mojo 1.0: len, abs, min, max, round, pow, sort, enumerate, zip, reversed, range — на месте. Есть и any с all, но принимают они коллекцию, а не отдельное значение:

def main():
var flags: List[Bool] = [True, False]
print(any(flags), all(flags))
Результат

True False

А вот привычного sum в языке нет. Сумму списка считают циклом:

def main():
var values: List[Int] = [3, 1, 2]
var total = 0
for value in values:
total += value
print(total)
Результат

6

🎯 Проверь себя

Как изменить элементы списка прямо в цикле?

Через for ref value in numbers: — тогда переменная цикла становится ссылкой на элемент, и value *= 10 меняет сам список. Без ref это копия, и изменения пропадут.

Почему `for city in codes: print(codes[city])` не компилируется?

Обращение к словарю по ключу может бросить исключение, если ключа нет. Значит, вызывающая функция должна быть помечена raises — либо нужно перебирать .items(), где ключ и значение приходят вместе.

Чем заменить `match` из Python 3.10?

Цепочкой if / elif / else: конструкции match в Mojo 1.0 нет. Если разбор идёт по типам, а не по значениям, лучше подойдёт перегрузка функций — она разрешается на этапе компиляции.

Обработка ошибок: raises, try / except и контекстные менеджеры.

Примеры проверены на Mojo 1.0.0

Тексты курса — CC BY-NC-SA 4.0, код примеров — Apache 2.0