Функции и замыкания

Освойте мощную систему функций Топаз. Изучите объявление функций, стрелочные функции, функции высшего порядка, замыкания и паттерны функционального программирования.

Функции являются гражданами первого класса в Топазе. Вы можете сохранять их в переменных, передавать в качестве параметров другим функциям и возвращать из функций. ��

🔧 Базовое объявление функций

Стандартное объявление функции

// Базовая структура функции
function приветствовать(имя: string) -> string {
    return "Привет, {имя}!"
}

// Функция без параметров
function получитьТекущееВремя() -> string {
    return "20 декабря 2024"
}

// Функция без возвращаемого значения (void)
function вывестиСообщение(сообщение: string) {
    print("Лог: {сообщение}")
}

// Примеры использования
let приветствие = приветствовать("Топаз Разработчик")
print(приветствие)  // "Привет, Топаз Разработчик!"

Параметры и значения по умолчанию

// Значения параметров по умолчанию
function вычислить(a: int, b: int = 10, операция: string = "сложение") -> int {
    match операция {
        case "сложение" => return a + b
        case "вычитание" => return a - b
        case "умножение" => return a * b
        case _ => return 0
    }
}

// Различные способы вызова
let результат1 = вычислить(5)                     // 5 + 10 = 15
let результат2 = вычислить(5, 3)                  // 5 + 3 = 8
let результат3 = вычислить(5, 3, "умножение")     // 5 * 3 = 15

// Именованные параметры
let результат4 = вычислить(a: 8, операция: "вычитание", b: 3)  // 8 - 3 = 5

Вариативные параметры

// Вариативные параметры (...rest)
function сумма(числа: ...int) -> int {
    let mut итого = 0
    for число in числа {
        итого += число
    }
    return итого
}

let итого1 = сумма(1, 2, 3, 4, 5)        // 15
let итого2 = сумма(10, 20)               // 30

// Комбинирование массива с дополнительными параметрами
function вычислитьСтатистику(метка: string, значения: ...float) -> string {
    let количество = значения.length()
    let среднее = значения.sum() / количество
    return "{метка}: количество={количество}, среднее={среднее}"
}

print(вычислитьСтатистику("Результаты тестов", 85.5, 92.0, 78.5, 89.0))

➡️ Стрелочные функции

Базовые стрелочные функции

// Простая стрелочная функция
let квадрат = (x: int) => x * x
let приветствие = (имя: string) => "Привет, {имя}!"

// Стрелочная функция без параметров
let случайноеЧисло = () => Math.random() * 100

// Сложная стрелочная функция
let информацияПользователя = (имя: string, возраст: int) => {
    let статус = if возраст >= 18 { "взрослый" } else { "несовершеннолетний" }
    return {
        имя: имя,
        возраст: возраст,
        статус: статус
    }
}

// Примеры использования
print(квадрат(5))                       // 25
print(приветствие("ТопазДев"))          // "Привет, ТопазДев!"
let информация = информацияПользователя("Иван", 25)
print(информация)                       // { имя: "Иван", возраст: 25, статус: "взрослый" }

Методы массивов со стрелочными функциями

let числа = [1, 2, 3, 4, 5]

// map преобразование
let квадраты = числа.map(x => x * x)
print(квадраты)  // [1, 4, 9, 16, 25]

// filter фильтрация
let четные = числа.filter(x => x % 2 == 0)
print(четные)  // [2, 4]

// reduce сведение
let итого = числа.reduce((аккум, текущий) => аккум + текущий, 0)
print(итого)   // 15

// Сложное связывание
let результат = числа
    .filter(x => x > 2)
    .map(x => x * 3)
    .reduce((a, b) => a + b, 0)
print(результат)   // (3 + 4 + 5) * 3 = 36

🎯 Функции высшего порядка

Функции как параметры

// Определение функции высшего порядка
function применитьОперацию(массив: [int], операция: function(int) -> int) -> [int] {
    let результат = []
    for элемент in массив {
        результат.push(операция(элемент))
    }
    return результат
}

// Передача функций как параметров
let числа = [1, 2, 3, 4]
let результатКвадратов = применитьОперацию(числа, x => x * x)
let результатУдвоения = применитьОперацию(числа, x => x * 2)

print(результатКвадратов)  // [1, 4, 9, 16]
print(результатУдвоения)   // [2, 4, 6, 8]

// Функция условной обработки
function условнаяОбработка(значение: int, условие: function(int) -> bool, обработчик: function(int) -> int) -> int {
    if условие(значение) {
        return обработчик(значение)
    }
    return значение
}

let результат = условнаяОбработка(15, x => x > 10, x => x * 2)
print(результат)  // 30 (поскольку 15 > 10, то 15 * 2)

Возврат функций

// Фабрика функций
function создатьУмножитель(множитель: int) -> function(int) -> int {
    return function(x: int) -> int {
        return x * множитель
    }
}

let удвоитель = создатьУмножитель(2)
let утроитель = создатьУмножитель(3)

print(удвоитель(5))  // 10
print(утроитель(4))  // 12

// Настраиваемая функция валидации
function создатьВалидаторДиапазона(мин: int, макс: int) -> function(int) -> bool {
    return function(значение: int) -> bool {
        return значение >= мин && значение <= макс
    }
}

let валидаторВозрастаВзрослых = создатьВалидаторДиапазона(18, 65)
let валидаторВозрастаСтудентов = создатьВалидаторДиапазона(5, 18)

print(валидаторВозрастаВзрослых(25))  // true
print(валидаторВозрастаСтудентов(16)) // true
print(валидаторВозрастаСтудентов(25)) // false

🔒 Замыкания

Базовые замыкания

function создатьСчетчик(начальноеЗначение: int) -> function() -> int {
    let mut счет = начальноеЗначение  // Захватывается замыканием
    
    return function() -> int {
        счет += 1      // Доступ к внешней переменной
        return счет
    }
}

let счетчик1 = создатьСчетчик(0)
let счетчик2 = создатьСчетчик(100)

print(счетчик1())  // 1
print(счетчик1())  // 2
print(счетчик2())  // 101
print(счетчик1())  // 3

Сложные паттерны замыканий

// Замыкание с состоянием
function создатьСчет(начальныйБаланс: float) -> { пополнить: function(float) -> float, снять: function(float) -> float, проверитьБаланс: function() -> float } {
    let mut баланс = начальныйБаланс
    
    return {
        пополнить: function(сумма: float) -> float {
            баланс += сумма
            return баланс
        },
        снять: function(сумма: float) -> float {
            if сумма <= баланс {
                баланс -= сумма
            }
            return баланс
        },
        проверитьБаланс: function() -> float {
            return баланс
        }
    }
}

let мойСчет = создатьСчет(1000.0)
print(мойСчет.пополнить(500.0))     // 1500.0
print(мойСчет.снять(200.0))         // 1300.0
print(мойСчет.проверитьБаланс())    // 1300.0

// Замыкание, захватывающее окружение
function создатьМенеджерКонфигурации(базоваяКонфигурация: { [string]: any }) -> function(string, any) -> any {
    let mut текущаяКонфигурация = базоваяКонфигурация.copy()
    
    return function(ключ: string, значение: any = null) -> any {
        if значение != null {
            текущаяКонфигурация[ключ] = значение
        }
        return текущаяКонфигурация[ключ]
    }
}

let конфигурацияПриложения = создатьМенеджерКонфигурации({
    тема: "темная",
    язык: "Русский",
    уведомления: true
})

print(конфигурацияПриложения("тема"))          // "темная"
конфигурацияПриложения("тема", "светлая")
print(конфигурацияПриложения("тема"))          // "светлая"

🔄 Рекурсивные функции

Базовая рекурсия

// Вычисление факториала
function факториал(n: int) -> int {
    if n <= 1 {
        return 1
    }
    return n * факториал(n - 1)
}

print(факториал(5))  // 120

// Последовательность Фибоначчи
function фибоначчи(n: int) -> int {
    if n <= 1 {
        return n
    }
    return фибоначчи(n - 1) + фибоначчи(n - 2)
}

print(фибоначчи(8))  // 21

// Сумма массива (рекурсивно)
function суммаМассива(массив: [int]) -> int {
    if массив.length() == 0 {
        return 0
    }
    if массив.length() == 1 {
        return массив[0]
    }
    return массив[0] + суммаМассива(массив.slice(1))
}

print(суммаМассива([1, 2, 3, 4, 5]))  // 15

Оптимизация хвостовой рекурсии

// Хвостово-рекурсивный факториал
function хвостовойФакториал(n: int, аккумулятор: int = 1) -> int {
    if n <= 1 {
        return аккумулятор
    }
    return хвостовойФакториал(n - 1, аккумулятор * n)  // Хвостовой вызов
}

// Хвостово-рекурсивный Фибоначчи
function хвостовойФибоначчи(n: int, a: int = 0, b: int = 1) -> int {
    if n == 0 {
        return a
    }
    return хвостовойФибоначчи(n - 1, b, a + b)  // Хвостовой вызов
}

print(хвостовойФакториал(10))  // 3628800
print(хвостовойФибоначчи(10))  // 55

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

Каррирование

// Каррированная функция
function сложить(a: int) -> function(int) -> int {
    return function(b: int) -> int {
        return a + b
    }
}

let прибавитьПять = сложить(5)
print(прибавитьПять(3))  // 8

// Многопараметровое каррирование
function умножитьИСложить(множитель: float) -> function(float) -> function(float) -> float {
    return function(слагаемое: float) -> function(float) -> float {
        return function(значение: float) -> float {
            return значение * множитель + слагаемое
        }
    }
}

let удвоитьИПрибавитьДесять = умножитьИСложить(2.0)(10.0)
print(удвоитьИПрибавитьДесять(5.0))  // 20.0 (5 * 2 + 10)

Композиция функций

// Утилита композиции функций
function составить<T, U, V>(f: function(U) -> V, g: function(T) -> U) -> function(T) -> V {
    return function(x: T) -> V {
        return f(g(x))
    }
}

let квадрат = (x: int) => x * x
let удвоить = (x: int) => x * 2
let вСтроку = (x: int) => x.toString()

// Составление функций
let квадратЗатемУдвоить = составить(удвоить, квадрат)
let квадратЗатемУдвоитьЗатемСтрока = составить(вСтроку, квадратЗатемУдвоить)

print(квадратЗатемУдвоить(3))         // 18 (3² * 2)
print(квадратЗатемУдвоитьЗатемСтрока(4))  // "32" (4² * 2 → строка)

Мемоизация

// Декоратор мемоизации
function мемоизировать<T, R>(функция: function(T) -> R) -> function(T) -> R {
    let mut кеш = {}
    
    return function(вход: T) -> R {
        let ключ = вход.toString()
        if ключ in кеш.keys {
            return кеш[ключ]
        }
        
        let результат = функция(вход)
        кеш[ключ] = результат
        return результат
    }
}

// Оптимизация медленного Фибоначчи с мемоизацией
let мемоФибоначчи = мемоизировать(function(n: int) -> int {
    if n <= 1 return n
    return мемоФибоначчи(n - 1) + мемоФибоначчи(n - 2)
})

// Теперь большие числа вычисляются быстро
print(мемоФибоначчи(40))  // Вычисляется быстро

🛡️ Оптимизация функций и лучшие практики

1. Использование чистых функций

// Хорошо: Чистая функция
function вычислитьНалог(доход: float, налоговаяСтавка: float) -> float {
    return доход * налоговаяСтавка
}

// Избегайте: Функция с побочными эффектами
let mut глобальныйСчетчик = 0
function плохаяФункция(значение: int) -> int {
    глобальныйСчетчик += 1  // Побочный эффект!
    return значение * 2
}

// Хорошая альтернатива: Возврат состояния
function хорошаяФункция(значение: int, текущийСчетчик: int) -> { результат: int, новыйСчетчик: int } {
    return {
        результат: значение * 2,
        новыйСчетчик: текущийСчетчик + 1
    }
}

2. Именование функций и структурирование

// Четкие имена функций
function валидироватьEmailПользователя(email: string) -> bool {
    return email.contains("@") && email.contains(".")
}

function вычислитьОбщуюСуммуЗаказа(элементыЗаказа: [{ цена: float, количество: int }]) -> float {
    return элементыЗаказа
        .map(элемент => элемент.цена * элемент.количество)
        .reduce((общее, сумма) => общее + сумма, 0.0)
}

// Принцип единственной ответственности
function разобратьДанныеПользователя(исходныеДанные: string) -> { имя: string, возраст: int } {
    // Отвечает только за разбор
    let части = исходныеДанные.split(",")
    return {
        имя: части[0].trim(),
        возраст: parseInt(части[1].trim())
    }
}

function валидироватьДанныеПользователя(пользователь: { имя: string, возраст: int }) -> bool {
    // Отвечает только за валидацию
    return пользователь.имя.length() > 0 && пользователь.возраст > 0
}

3. Обработка ошибок

// Безопасная функция с типом Result
function безопасноеДеление(числитель: float, знаменатель: float) -> Result<float, string> {
    if знаменатель == 0.0 {
        return Err("Нельзя делить на ноль")
    }
    return Ok(числитель / знаменатель)
}

// Использование
match безопасноеДеление(10.0, 2.0) {
    case Ok(результат) => print("Результат: {результат}")
    case Err(ошибка) => print("Ошибка: {ошибка}")
}

Функции и замыкания являются основными возможностями Топаз. Их эффективное использование позволяет писать переиспользуемый и тестируемый код! 🎯