Функции
This content is not available in your language yet.
Большая часть того, что вы знаете о функциях из Python, работает и здесь. Но есть одно понятие, которого в Python нет вовсе, — и именно с него начинается настоящая сила Mojo.
Сигнатура
Заголовок раздела «Сигнатура»def имя[параметры](аргументы) -> тип_возврата: телоОбязательно только имя и тело. Всё остальное — по необходимости, кроме типов аргументов: они нужны всегда.
def greet(name: String, greeting: String = "Привет") -> String: return greeting + ", " + name + "!"Функция без возвращаемого значения просто не имеет стрелки, либо пишется
-> None — оба варианта равнозначны.
Аргументы и параметры — это разные вещи
Заголовок раздела «Аргументы и параметры — это разные вещи»В Python «аргумент» и «параметр» — синонимы, и путать их не страшно. В Mojo это два разных механизма, и разница принципиальная:
| Аргументы | Параметры | |
|---|---|---|
| Скобки | круглые ( ) | квадратные [ ] |
| Когда известны | во время выполнения | на этапе компиляции |
| Что с ними делает компилятор | передаёт в функцию | генерирует отдельную версию функции |
def repeat[count: Int](text: String): for _ in range(count): print(text, end=" ") print()
def main(): repeat[3](String("эй"))эй эй эй
Здесь count — параметр: компилятор знает, что повторов ровно три, и может
развернуть цикл в три вызова print без всякого счётчика. text — обычный
аргумент, его значение станет известно только при запуске.
Значения по умолчанию и именованные аргументы
Заголовок раздела «Значения по умолчанию и именованные аргументы»Работают как в Python:
greet("Мир") # позиционноgreet(name="Mojo", greeting="Здравствуй") # по именамОбязательные аргументы идут перед аргументами со значением по умолчанию. Можно сделать аргумент только именованным, поставив перед ним звёздочку:
def connect(host: String, *, timeout: Int = 30) -> String: return host + ":" + String(timeout)
def main(): print(connect("example.com", timeout=5))example.com:5
Такие аргументы нельзя передать позиционно — это защита от вызовов вида
connect("example.com", 5, True, False), где через полгода никто не помнит,
что означает каждое число.
Вариативные аргументы
Заголовок раздела «Вариативные аргументы»def total(*values: Int) -> Int: var sum = 0 for value in values: sum += value return sum
def main(): print(total(1, 2, 3, 4))10
Обратите внимание: у вариативных аргументов тоже есть тип. *values: Int
означает «сколько угодно целых», а не «сколько угодно чего угодно».
Перегрузка
Заголовок раздела «Перегрузка»Одно имя может обслуживать разные типы — компилятор выберет нужную версию по типам аргументов:
def describe(value: Int): print("целое:", value)
def describe(value: String): print("строка:", value)
def main(): describe(42) describe(String("сорок два"))целое: 42 строка: сорок два
Это не «магия»: выбор происходит на этапе компиляции, накладных расходов при вызове нет.
error: redefinition of function 'show' with identical signature
Две функции с одним именем и одинаковыми типами аргументов. Компилятору нечем их различить.
Перегрузка различает функции по типам аргументов, а не по именам или типу возврата. Дайте разные имена либо разные типы.
Возврат нескольких значений
Заголовок раздела «Возврат нескольких значений»Отдельного синтаксиса нет — используется кортеж:
def divide(a: Int, b: Int) -> Tuple[Int, Int]: return (a // b, a % b)
def main(): var result = divide(17, 5) print(result[0], result[1])3 2
Что функция делает с аргументом
Заголовок раздела «Что функция делает с аргументом»По умолчанию аргумент передаётся только для чтения: функция видит
значение, но изменить его у вызывающего кода не может. Если менять нужно —
пишется mut:
def bump(mut counter: Int): counter += 1
def main(): var counter = 10 bump(counter) print(counter)11
Кроме mut есть ещё var (передача владения), ref и out — им посвящена
отдельная глава, потому что за ними стоит вся
модель памяти языка.
error: 'mut' arguments must not have defaults
У изменяемого аргумента нельзя задать значение по умолчанию.
Логично: менять нечего, если аргумент не передали. Уберите значение по умолчанию либо сделайте аргумент обычным.
Вложенные функции и рекурсия
Заголовок раздела «Вложенные функции и рекурсия»Обе привычные возможности на месте:
def factorial(n: Int) -> Int: if n <= 1: return 1 return n * factorial(n - 1)
def main(): def double(x: Int) -> Int: return x * 2
print(double(21), factorial(10))42 3628800
Функции, которые могут бросить исключение
Заголовок раздела «Функции, которые могут бросить исключение»Помечаются словом raises:
def parse(text: String) raises -> Int: return Int(text)Вызывающий код обязан это учесть: либо обернуть вызов в try, либо тоже
объявить себя raises. В Python исключение может прилететь откуда угодно;
в Mojo способность бросать исключение — часть контракта функции.
Докстринги проверяет компилятор
Заголовок раздела «Докстринги проверяет компилятор»Приятная мелочь, которой нет в Python:
warning: doc string summary should begin with a capital letteror non-alpha character, but this begins with 'm'Mojo следит за оформлением документации к функциям — и подсказывает, если первая строка докстринга написана небрежно. Мелочь, а дисциплинирует.
Всё вместе
Заголовок раздела «Всё вместе»def greet(name: String, greeting: String = "Привет") -> String: """Значение по умолчанию и именованные аргументы — как в Python.""" return greeting + ", " + name + "!"
def describe(value: Int): """Перегрузка: одно имя, разные типы аргумента.""" print("целое:", value)
def describe(value: String): print("строка:", value)
def divide(a: Int, b: Int) -> Tuple[Int, Int]: """Вернуть несколько значений можно кортежем.""" return (a // b, a % b)
def total(*values: Int) -> Int: """Вариативные аргументы: сколько угодно чисел.""" var sum = 0 for value in values: sum += value return sum
def bump(mut counter: Int): """Аргумент mut означает, что функция меняет переменную вызывающего кода.""" counter += 1
def repeat[count: Int](text: String): """Параметр count известен на этапе компиляции — он в квадратных скобках.""" for _ in range(count): print(text, end=" ") print()
def main(): print(greet("Мир")) print(greet(name="Mojo", greeting="Здравствуй"))
describe(42) describe(String("сорок два"))
var result = divide(17, 5) print("частное и остаток:", result[0], result[1])
print("сумма:", total(1, 2, 3, 4))
var counter = 10 bump(counter) print("после bump:", counter)
repeat[3](String("эй"))Привет, Мир! Здравствуй, Mojo! целое: 42 строка: сорок два частное и остаток: 3 2 сумма: 10 после bump: 11 эй эй эй
🎯 Проверь себя
Чем параметр в квадратных скобках отличается от аргумента в круглых?
Параметр известен на этапе компиляции, и компилятор генерирует под каждое его значение отдельную специализированную версию функции. Аргумент передаётся во время выполнения. Из этого различия и растёт производительность Mojo.
Можно ли перегрузить функцию, изменив только тип возвращаемого значения?
Нет. Перегрузка различает функции по типам аргументов. Две функции
с одинаковыми аргументами дадут ошибку redefinition of function with identical signature, даже если возвращают разное.
Как вернуть из функции два значения?
Кортежем: -> Tuple[Int, Int] и return (a, b). Отдельного синтаксиса
множественного возврата, как и в Python, нет — там тоже возвращается
кортеж, просто скобки не пишут.
Что дальше
Заголовок раздела «Что дальше»Операторы: арифметика, сравнения и то, как научить
свою структуру складываться через +.
Тексты курса — CC BY-NC-SA 4.0, код примеров — Apache 2.0