Топаз предоставляет мощную библиотеку функционального программирования для поддержки декларативного и выразительного написания кода. Используйте все функциональные инструменты на полную мощность! 🚀
🎯 Функции высшего порядка
Базовые функции высшего порядка
// Функция, принимающая функцию как аргумент
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Квадратов}")
Функциональная библиотека Топаз обеспечивает декларативное и выразительное программирование. Используйте функции высшего порядка, монады, ленивые вычисления и многое другое для написания более элегантного и безопасного кода! ✨