Функциональная библиотека

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

Топаз предоставляет мощную библиотеку функционального программирования для поддержки декларативного и выразительного написания кода. Используйте все функциональные инструменты на полную мощность! 🚀

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

Базовые функции высшего порядка

// Функция, принимающая функцию как аргумент
function дваждыПрименить<T>(функция: (T) -> T, значение: T) -> T {
    return функция(функция(значение))
}

let удвоить = (x: int) => x * 2
let результат = дваждыПрименить(удвоить, 5)                    // 20

// Функция, возвращающая функцию
function множитель(коэффициент: int) -> (int) -> int {
    return (значение: int) => значение * коэффициент
}

let утроитель = множитель(3)
let результатУтроения = утроитель(7)                          // 21

// Условное применение функции
function условноеПрименение<T>(
    условие: (T) -> bool,
    функция: (T) -> T,
    значение: T
) -> T {
    if условие(значение) {
        return функция(значение)
    } else {
        return значение
    }
}

let удвоитьЕслиЧетное = условноеПрименение(
    x => x % 2 == 0,
    x => x * 2,
    8
)                                                             // 16

print("Результат: {результат}")
print("Результат утроения: {результатУтроения}")
print("Удвоить если четное: {удвоитьЕслиЧетное}")

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

// Базовая композиция функций
function композиция<A, B, C>(
    f: (B) -> C,
    g: (A) -> B
) -> (A) -> C {
    return (входные: A) => f(g(входные))
}

let плюсОдин = (x: int) => x + 1
let умножитьНаДва = (x: int) => x * 2
let составнаяФункция = композиция(умножитьНаДва, плюсОдин)     // (x + 1) * 2

let результатКомпозиции = составнаяФункция(5)                 // 12

// Композиция множественных функций
function конвейер<T>(...функции: Array<(T) -> T>) -> (T) -> T {
    return (начальноеЗначение: T) => {
        return функции.reduce((аккумулятор, текущаяФункция) => текущаяФункция(аккумулятор), начальноеЗначение)
    }
}

let сложнаяОбработка = конвейер(
    (x: int) => x + 1,        // +1
    (x: int) => x * 2,        // *2
    (x: int) => x - 3,        // -3
    (x: int) => x / 2         // /2
)

let результатКонвейера = сложнаяОбработка(10)                 // 9.5

print("Результат композиции: {результатКомпозиции}")
print("Результат конвейера: {результатКонвейера}")

Частичное применение

// Функция частичного применения
function частичноеПрименение<A, B, C>(
    функция: (A, B) -> C,
    первыйАргумент: A
) -> (B) -> C {
    return (второйАргумент: B) => функция(первыйАргумент, второйАргумент)
}

let сложить = (a: int, b: int) => a + b
let прибавить10 = частичноеПрименение(сложить, 10)
let результатЧастичногоПрименения = прибавить10(5)           // 15

// Частичное применение трех аргументов
function частичное3<A, B, C, D>(
    функция: (A, B, C) -> D,
    первый: A,
    второй: B
) -> (C) -> D {
    return (третий: C) => функция(первый, второй, третий)
}

let вычислить = (x: int, y: int, z: int) => x * y + z
let частичноеВычисление = частичное3(вычислить, 2, 3)         // 2 * 3 + z
let финальныйРезультат = частичноеВычисление(4)               // 10

print("Результат частичного применения: {результатЧастичногоПрименения}")
print("Финальный результат: {финальныйРезультат}")

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

Базовое каррирование

// Ручное каррирование
function карри2<A, B, C>(функция: (A, B) -> C) -> (A) -> (B) -> C {
    return (a: A) => (b: B) => функция(a, b)
}

function карри3<A, B, C, D>(функция: (A, B, C) -> D) -> (A) -> (B) -> (C) -> D {
    return (a: A) => (b: B) => (c: C) => функция(a, b, c)
}

// Применение каррирования
let сложить = (x: int, y: int) => x + y
let каррированноеСложение = карри2(сложить)
let прибавить5 = каррированноеСложение(5)
let результатКарри = прибавить5(3)                           // 8

// Каррирование трех аргументов
let формула = (x: int, y: int, z: int) => x * y + z
let каррированнаяФормула = карри3(формула)
let пошаговоеВычисление = каррированнаяФормула(2)(3)(4)      // 10

print("Результат карри: {результатКарри}")
print("Пошаговое вычисление: {пошаговоеВычисление}")

Автоматическое каррирование

// Декоратор автоматического каррирования
function автоКарри<T>(функция: Function) -> Function {
    // Проверка количества аргументов во время выполнения и автоматическое каррирование
    return function(...аргументы: Array<T>) {
        if аргументы.length >= функция.arity {
            return функция.apply(null, аргументы)
        } else {
            return function(...дополнительныеАргументы: Array<T>) {
                return автоКарри(функция).apply(null, [...аргументы, ...дополнительныеАргументы])
            }
        }
    }
}

// Пример использования
let умножить = автоКарри((a: int, b: int, c: int) => a * b * c)

let способ1 = умножить(2, 3, 4)                              // 24 (все аргументы сразу)
let способ2 = умножить(2)(3)(4)                              // 24 (последовательное применение)
let способ3 = умножить(2, 3)(4)                              // 24 (частичное применение)

let частичноеУмножение = умножить(2, 3)                       // Возвращает функцию
let полноеУмножение = частичноеУмножение(4)                   // 24

print("Результаты автокарри: {способ1}, {способ2}, {способ3}")

🔄 Утилиты преобразования функций

Мемоизация

// Базовая мемоизация
function мемоизировать<T, R>(функция: (T) -> R) -> (T) -> R {
    let mut кэш: Map<T, R> = Map.new()
    
    return (входные: T) => {
        if кэш.has(входные) {
            return кэш.get(входные).unwrap()
        }
        
        let результат = функция(входные)
        кэш.set(входные, результат)
        return результат
    }
}

// Мемоизация для чисел Фибоначчи
let фибоначчи = мемоизировать((n: int) => {
    print("Вычисляем: фибоначчи({n})")
    if n <= 1 { return n }
    return фибоначчи(n - 1) + фибоначчи(n - 2)
})

let фиб10 = фибоначчи(10)                                    // 55 (без дублирующих вычислений)
let фиб10Снова = фибоначчи(10)                              // 55 (возвращено из кэша мгновенно)

// Мемоизация с TTL (Time To Live)
function мемоизацияСTTL<T, R>(
    функция: (T) -> R,
    ttl: int
) -> (T) -> R {
    let mut кэш: Map<T, { результат: R, временнаяМетка: int }> = Map.new()
    
    return (входные: T) => {
        let текущееВремя = Date.now()
        
        if кэш.has(входные) {
            let записьКэша = кэш.get(входные).unwrap()
            if (текущееВремя - записьКэша.временнаяМетка) < ttl {
                return записьКэша.результат
            }
        }
        
        let результат = функция(входные)
        кэш.set(входные, { результат, временнаяМетка: текущееВремя })
        return результат
    }
}

print("Фибоначчи 10: {фиб10}")

Устранение дребезга и дросселирование

// Функция устранения дребезга
function устранитьДребезг<T>(
    функция: (...T) -> void,
    задержка: int
) -> (...T) -> void {
    let mut idТаймера: Option<int> = None
    
    return (...аргументы: Array<T>) => {
        // Отменить существующий таймер
        match idТаймера {
            case Some(id) => clearTimeout(id)
            case None => {}
        }
        
        // Установить новый таймер
        idТаймера = Some(setTimeout(() => {
            функция(...аргументы)
            idТаймера = None
        }, задержка))
    }
}

// Функция дросселирования
function дросселировать<T>(
    функция: (...T) -> void,
    интервал: int
) -> (...T) -> void {
    let mut времяПоследнегоВызова = 0
    let mut idТаймера: Option<int> = None
    
    return (...аргументы: Array<T>) => {
        let текущееВремя = Date.now()
        let времяСПоследнегоВызова = текущееВремя - времяПоследнегоВызова
        
        if времяСПоследнегоВызова >= интервал {
            функция(...аргументы)
            времяПоследнегоВызова = текущееВремя
        } else if idТаймера.isNone() {
            idТаймера = Some(setTimeout(() => {
                функция(...аргументы)
                времяПоследнегоВызова = Date.now()
                idТаймера = None
            }, интервал - времяСПоследнегоВызова))
        }
    }
}

// Примеры использования
let функцияПоиска = (запрос: string) => {
    print("Выполняем поиск: {запрос}")
}

let поискСУстранениемДребезга = устранитьДребезг(функцияПоиска, 300)   // Выполнить через 300мс
let дроссельныйПоиск = дросселировать(функцияПоиска, 1000)           // Максимум 1 вызов в секунду

🎭 Функциональное преобразование данных

Линзы

// Базовая реализация линз
struct Линза<S, A> {
    получить: (S) -> A,
    установить: (A, S) -> S
}

impl<S, A> Линза<S, A> {
    function new(получатель: (S) -> A, установщик: (A, S) -> S) -> Линза<S, A> {
        return Линза { получить: получатель, установить: установщик }
    }
    
    function скомпоновать<B>(другая: Линза<A, B>) -> Линза<S, B> {
        return Линза.new(
            (s: S) => другая.получить(self.получить(s)),
            (b: B, s: S) => {
                let a = self.получить(s)
                let новоеA = другая.установить(b, a)
                return self.установить(новоеA, s)
            }
        )
    }
    
    function изменить(функция: (A) -> A, структура: S) -> S {
        let текущееЗначение = self.получить(структура)
        let новоеЗначение = функция(текущееЗначение)
        return self.установить(новоеЗначение, структура)
    }
}

// Структура пользователя
struct Пользователь {
    имя: string,
    возраст: int,
    адрес: Адрес
}

struct Адрес {
    город: string,
    почтовыйИндекс: string
}

// Определения линз
let линзаИмени = Линза.new(
    (пользователь: Пользователь) => пользователь.имя,
    (имя: string, пользователь: Пользователь) => пользователь{ имя }
)

let линзаАдреса = Линза.new(
    (пользователь: Пользователь) => пользователь.адрес,
    (адрес: Адрес, пользователь: Пользователь) => пользователь{ адрес }
)

let линзаГорода = Линза.new(
    (адрес: Адрес) => адрес.город,
    (город: string, адрес: Адрес) => адрес{ город }
)

// Композиция линз
let линзаГородаПользователя = линзаАдреса.скомпоновать(линзаГорода)

// Пример использования
let пользователь = Пользователь {
    имя: "Иван Иванов",
    возраст: 30,
    адрес: Адрес { город: "Москва", почтовыйИндекс: "123456" }
}

let новыйПользователь = линзаГородаПользователя.установить("Санкт-Петербург", пользователь)
let измененныйПользователь = линзаИмени.изменить(имя => имя.toUpperCase(), новыйПользователь)

print("Исходный город: {линзаГородаПользователя.получить(пользователь)}")      // "Москва"
print("Новый город: {линзаГородаПользователя.получить(новыйПользователь)}")   // "Санкт-Петербург"

Конвейер функциональных данных

// Конвейер преобразования данных
struct Конвейер<T> {
    данные: T
}

impl<T> Конвейер<T> {
    function из(данные: T) -> Конвейер<T> {
        return Конвейер { данные }
    }
    
    function мапить<U>(преобразование: (T) -> U) -> Конвейер<U> {
        return Конвейер.из(преобразование(self.данные))
    }
    
    function фильтровать(условие: (T) -> bool) -> Конвейер<Option<T>> {
        if условие(self.данные) {
            return Конвейер.из(Some(self.данные))
        } else {
            return Конвейер.из(None)
        }
    }
    
    function выполнить(побочныйЭффект: (T) -> void) -> Конвейер<T> {
        побочныйЭффект(self.данные)
        return self
    }
    
    function получитьЗначение() -> T {
        return self.данные
    }
}

// Пример обработки данных
let результат = Конвейер.из("  Привет Мир  ")
    .мапить(s => s.trim())
    .выполнить(s => print("После обрезки: '{s}'"))
    .мапить(s => s.toLowerCase())
    .выполнить(s => print("В нижнем регистре: '{s}'"))
    .мапить(s => s.split(" "))
    .мапить(массив => массив.join("-"))
    .получитьЗначение()                               // "привет-мир"

print("Финальный результат: {результат}")

🏗️ Монады

Монада Maybe/Option

// Помощники Option в виде функций пространства имён
module Option {
    function мапить<T, U>(опция: Option<T>, преобразование: (T) -> U) -> Option<U> {
        match опция {
            case Some(значение) => Some(преобразование(значение))
            case None => None
        }
    }

    function плоскоМапить<T, U>(опция: Option<T>, функция: (T) -> Option<U>) -> Option<U> {
        match опция {
            case Some(значение) => функция(значение)
            case None => None
        }
    }

    function фильтровать<T>(опция: Option<T>, условие: (T) -> bool) -> Option<T> {
        match опция {
            case Some(значение) if условие(значение) => Some(значение)
            case _ => None
        }
    }

    function илиИначе<T>(опция: Option<T>, альтернатива: () -> Option<T>) -> Option<T> {
        match опция {
            case Some(_) => опция
            case None => альтернатива()
        }
    }
}

// Пример цепочки вызовов
let данныеПользователя = Some("Иван Иванов")

let обработанныйРезультат = данныеПользователя
    |> Option.фильтровать(_, имя => имя.length > 2)
    |> Option.мапить(_, имя => имя.toUpperCase())
    |> Option.плоскоМапить(_, имя => {
        if имя.startsWith("ИВАН") {
            return Some("Приветствие: Привет {имя}")
        } else {
            return None
        }
    })
    |> Option.илиИначе(_, () => Some("Неизвестный пользователь"))

print("Обработанный результат: {обработанныйРезультат}")      // Some("Приветствие: Привет ИВАН ИВАНОВ")

Монада Result

// Расширение монады Result
impl<T, E> Result<T, E> {
    function плоскоМапить<U>(функция: (T) -> Result<U, E>) -> Result<U, E> {
        match self {
            case Ok(значение) => функция(значение)
            case Err(ошибка) => Err(ошибка)
        }
    }
    
    function мапитьОшибку<F>(функция: (E) -> F) -> Result<T, F> {
        match self {
            case Ok(значение) => Ok(значение)
            case Err(ошибка) => Err(функция(ошибка))
        }
    }
    
    function восстановить(функцияВосстановления: (E) -> T) -> T {
        match self {
            case Ok(значение) => значение
            case Err(ошибка) => функцияВосстановления(ошибка)
        }
    }
}

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

function безопасныйКореньКвадратный(значение: float) -> Result<float, string> {
    if значение < 0.0 {
        return Err("Нельзя вычислить квадратный корень отрицательного числа")
    }
    return Ok(Math.sqrt(значение))
}

let результатВычисления = Ok(16.0)
    .плоскоМапить(значение => безопасноеДеление(значение, 4.0))    // 16 / 4 = 4
    .плоскоМапить(значение => безопасныйКореньКвадратный(значение)) // √4 = 2
    .мапить(значение => значение * 2)                               // 2 * 2 = 4
    .восстановить(ошибка => {
        print("Произошла ошибка: {ошибка}")
        return 0.0
    })

print("Результат вычисления: {результатВычисления}")             // 4.0

🎨 Функциональные паттерны

Паттерн стратегия

// Функциональный паттерн стратегия
enum СтратегияСортировки {
    БыстраяСортировка,
    СортировкаСлиянием,
    СортировкаПузырьком
}

function сортироватьСо<T>(
    массив: Array<T>,
    стратегия: СтратегияСортировки,
    функцияСравнения: (T, T) -> int
) -> Array<T> {
    let функцияСортировки = match стратегия {
        case БыстраяСортировка => быстраяСортировка
        case СортировкаСлиянием => сортировкаСлиянием
        case СортировкаПузырьком => сортировкаПузырьком
    }
    
    return функцияСортировки(массив, функцияСравнения)
}

function быстраяСортировка<T>(массив: Array<T>, сравнить: (T, T) -> int) -> Array<T> {
    // Реализация быстрой сортировки
    if массив.length <= 1 { return массив }
    
    let опорныйЭлемент = массив[массив.length / 2]
    let меньшие = массив.filter(x => сравнить(x, опорныйЭлемент) < 0)
    let равные = массив.filter(x => сравнить(x, опорныйЭлемент) == 0)
    let большие = массив.filter(x => сравнить(x, опорныйЭлемент) > 0)
    
    return [
        ...быстраяСортировка(меньшие, сравнить),
        ...равные,
        ...быстраяСортировка(большие, сравнить)
    ]
}

// Пример использования
let массивЧисел = [64, 34, 25, 12, 22, 11, 90]
let отсортированныйМассив = сортироватьСо(
    массивЧисел,
    БыстраяСортировка,
    (a, b) => a - b
)

print("Отсортированный массив: {отсортированныйМассив}")

Паттерн наблюдатель

// Функциональный паттерн наблюдатель
struct Наблюдаемый<T> {
    подписчики: Array<(T) -> void>
}

impl<T> Наблюдаемый<T> {
    function new() -> Наблюдаемый<T> {
        return Наблюдаемый { подписчики: [] }
    }
    
    function подписаться(колбэк: (T) -> void) -> () -> void {
        self.подписчики.push(колбэк)
        
        // Возвращаем функцию отписки
        return () => {
            self.подписчики = self.подписчики.filter(п => п != колбэк)
        }
    }
    
    function отправить(значение: T) {
        self.подписчики.forEach(колбэк => колбэк(значение))
    }
    
    function мапить<U>(преобразование: (T) -> U) -> Наблюдаемый<U> {
        let новыйНаблюдаемый = Наблюдаемый.new()
        
        self.подписаться(значение => {
            новыйНаблюдаемый.отправить(преобразование(значение))
        })
        
        return новыйНаблюдаемый
    }
    
    function фильтровать(условие: (T) -> bool) -> Наблюдаемый<T> {
        let новыйНаблюдаемый = Наблюдаемый.new()
        
        self.подписаться(значение => {
            if условие(значение) {
                новыйНаблюдаемый.отправить(значение)
            }
        })
        
        return новыйНаблюдаемый
    }
}

// Пример использования
let потокКликов = Наблюдаемый.new()

let отписка1 = потокКликов
    .фильтровать(событие => событие.тип == "клик")
    .мапить(событие => событие.координаты)
    .подписаться(координаты => print("Позиция клика: {координаты}"))

let отписка2 = потокКликов
    .фильтровать(событие => событие.двойнойКлик)
    .подписаться(событие => print("Обнаружен двойной клик"))

// Отправка событий
потокКликов.отправить({ тип: "клик", координаты: [100, 200], двойнойКлик: false })
потокКликов.отправить({ тип: "клик", координаты: [300, 400], двойнойКлик: true })

// Позже отписаться
отписка1()
отписка2()

🚀 Продвинутые функциональные техники

Y комбинатор

// Y комбинатор для реализации рекурсивных функций
function Y<T>(функция: ((T) -> T) -> (T) -> T) -> (T) -> T {
    return function(аргумент: T) -> T {
        return функция(Y(функция))(аргумент)
    }
}

// Факториал через Y комбинатор
let факториал = Y((рекурсивнаяФункция: (int) -> int) => (n: int) => {
    if n <= 1 {
        return 1
    } else {
        return n * рекурсивнаяФункция(n - 1)
    }
})

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

// Фибоначчи через Y комбинатор
let фибоначчиY = Y((рекурсивнаяФункция: (int) -> int) => (n: int) => {
    if n <= 1 {
        return n
    } else {
        return рекурсивнаяФункция(n - 1) + рекурсивнаяФункция(n - 2)
    }
})

let фиб8 = фибоначчиY(8)                              // 21

print("Y комбинатор факториал(5): {факториал5}")
print("Y комбинатор фибоначчи(8): {фиб8}")

Ленивые вычисления

// Структура ленивых вычислений
struct Ленивый<T> {
    функцияВычисления: () -> T,
    кэш: Option<T>
}

impl<T> Ленивый<T> {
    function new(функцияВычисления: () -> T) -> Ленивый<T> {
        return Ленивый { функцияВычисления, кэш: None }
    }
    
    function получить() -> T {
        match self.кэш {
            case Some(значение) => значение
            case None => {
                let вычисленноеЗначение = self.функцияВычисления()
                self.кэш = Some(вычисленноеЗначение)
                return вычисленноеЗначение
            }
        }
    }
    
    function мапить<U>(преобразование: (T) -> U) -> Ленивый<U> {
        return Ленивый.new(() => преобразование(self.получить()))
    }
}

// Бесконечная последовательность
struct ЛениваяПоследовательностьИсленныхчисел {
    генератор: (int) -> T
}

impl<T> ЛениваяПоследовательность<T> {
    function new(генератор: (int) -> T) -> ЛениваяПоследовательность<T> {
        return ЛениваяПоследовательность { генератор }
    }
    
    function взять(количество: int) -> Array<T> {
        let mut результат: Array<T> = []
        for i in 0..количество {
            результат.push(self.генератор(i))
        }
        return результат
    }
    
    function мапить<U>(преобразование: (T) -> U) -> ЛениваяПоследовательность<U> {
        return ЛениваяПоследовательность.new(индекс => преобразование(self.генератор(индекс)))
    }
    
    function фильтровать(условие: (T) -> bool) -> ЛениваяПоследовательность<T> {
        return ЛениваяПоследовательность.new(индекс => {
            let mut текущийИндекс = индекс
            let mut найденныйСчетчик = 0
            
            while true {
                let значение = self.генератор(текущийИндекс)
                if условие(значение) {
                    if найденныйСчетчик == индекс {
                        return значение
                    }
                    найденныйСчетчик += 1
                }
                текущийИндекс += 1
            }
        })
    }
}

// Бесконечная последовательность натуральных чисел
let натуральныеЧисла = ЛениваяПоследовательность.new(i => i + 1)
let первые10Натуральных = натуральныеЧисла.взять(10)         // [1, 2, 3, ..., 10]

// Извлечение только четных чисел
let четнаяПоследовательность = натуральныеЧисла.фильтровать(x => x % 2 == 0)
let первые5Четных = четнаяПоследовательность.взять(5)        // [2, 4, 6, 8, 10]

// Последовательность квадратов
let последовательностьКвадратов = натуральныеЧисла.мапить(x => x * x)
let первые5Квадратов = последовательностьКвадратов.взять(5) // [1, 4, 9, 16, 25]

print("Первые 10 натуральных: {первые10Натуральных}")
print("Первые 5 четных: {первые5Четных}")
print("Первые 5 квадратов: {первые5Квадратов}")

Функциональная библиотека Топаз обеспечивает декларативное и выразительное программирование. Используйте функции высшего порядка, монады, ленивые вычисления и многое другое для написания более элегантного и безопасного кода! ✨