Топаз предоставляет мощную библиотеку функционального программирования для поддержки декларативного и выразительного написания кода. Используйте все функциональные инструменты на полную мощность!
Функции высшего порядка
Базовые функции высшего порядка
// Функция, принимающая функцию как аргумент
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 вызов в секунду
Функциональное преобразование данных
Линзы
// Базовая реализация линз с явными helper-функциями
struct Линза<S, A> {
получить: (S) -> A,
установить: (A, S) -> S
}
function создатьЛинзу<S, A>(получатель: (S) -> A, установщик: (A, S) -> S) -> Линза<S, A> {
return Линза { получить: получатель, установить: установщик }
}
function скомпоноватьЛинзы<S, A, B>(внешняя: Линза<S, A>, внутренняя: Линза<A, B>) -> Линза<S, B> {
return создатьЛинзу(
(значение: S) => внутренняя.получить(внешняя.получить(значение)),
(замена: B, значение: S) => {
let текущийФокус = внешняя.получить(значение)
let новыйФокус = внутренняя.установить(замена, текущийФокус)
return внешняя.установить(новыйФокус, значение)
}
)
}
function изменитьЧерезЛинзу<S, A>(линза: Линза<S, A>, функция: (A) -> A, значение: S) -> S {
let текущееЗначение = линза.получить(значение)
return линза.установить(функция(текущееЗначение), значение)
}
// Структура пользователя
struct Пользователь {
имя: string,
возраст: int,
адрес: Адрес
}
struct Адрес {
город: string,
почтовыйИндекс: string
}
// Определения линз
let линзаИмени = создатьЛинзу(
(пользователь: Пользователь) => пользователь.имя,
(имя: string, пользователь: Пользователь) => пользователь{ имя }
)
let линзаАдреса = создатьЛинзу(
(пользователь: Пользователь) => пользователь.адрес,
(адрес: Адрес, пользователь: Пользователь) => пользователь{ адрес }
)
let линзаГорода = создатьЛинзу(
(адрес: Адрес) => адрес.город,
(город: string, адрес: Адрес) => адрес{ город }
)
// Композиция линз
let линзаГородаПользователя = скомпоноватьЛинзы(линзаАдреса, линзаГорода)
// Пример использования
let пользователь = Пользователь {
имя: "Иван Иванов",
возраст: 30,
адрес: Адрес { город: "Москва", почтовыйИндекс: "123456" }
}
let новыйПользователь = линзаГородаПользователя.установить("Санкт-Петербург", пользователь)
let измененныйПользователь = изменитьЧерезЛинзу(линзаИмени, имя => имя.toUpperCase(), новыйПользователь)
print("Исходный город: {линзаГородаПользователя.получить(пользователь)}") // "Москва"
print("Новый город: {линзаГородаПользователя.получить(новыйПользователь)}") // "Санкт-Петербург"
print("Измененное имя: {измененныйПользователь.имя}") // "ИВАН ИВАНОВ"
Конвейер функциональных данных
// Конвейер преобразования данных
struct Конвейер<T> {
данные: T
}
function конвейерИз<T>(данные: T) -> Конвейер<T> {
return Конвейер { данные }
}
function конвейерМап<T, U>(конвейер: Конвейер<T>, преобразование: (T) -> U) -> Конвейер<U> {
return Конвейер { данные: преобразование(конвейер.данные) }
}
function конвейерФильтр<T>(конвейер: Конвейер<T>, условие: (T) -> bool) -> Конвейер<Option<T>> {
if условие(конвейер.данные) {
return Конвейер { данные: Some(конвейер.данные) }
} else {
return Конвейер { данные: None }
}
}
function конвейерЭффект<T>(конвейер: Конвейер<T>, побочныйЭффект: (T) -> void) -> Конвейер<T> {
побочныйЭффект(конвейер.данные)
return конвейер
}
function значениеКонвейера<T>(конвейер: Конвейер<T>) -> T {
return конвейер.данные
}
// Пример обработки данных
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
// Helper-функции для Result в пространстве имён
module ResultИнструменты {
function map<T, U, E>(результат: Result<T, E>, преобразование: (T) -> U) -> Result<U, E> {
match результат {
case Ok(значение) => Ok(преобразование(значение))
case Err(ошибка) => Err(ошибка)
}
}
function flatMap<T, U, E>(результат: Result<T, E>, функция: (T) -> Result<U, E>) -> Result<U, E> {
match результат {
case Ok(значение) => функция(значение)
case Err(ошибка) => Err(ошибка)
}
}
function mapError<T, E, F>(результат: Result<T, E>, функция: (E) -> F) -> Result<T, F> {
match результат {
case Ok(значение) => Ok(значение)
case Err(ошибка) => Err(функция(ошибка))
}
}
function recover<T, E>(результат: Result<T, E>, функцияВосстановления: (E) -> T) -> T {
match результат {
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)
|> ResultИнструменты.flatMap(_, значение => безопасноеДеление(значение, 4.0)) // 16 / 4 = 4
|> ResultИнструменты.flatMap(_, значение => безопасныйКореньКвадратный(значение)) // √4 = 2
|> ResultИнструменты.map(_, значение => значение * 2) // 2 * 2 = 4
|> ResultИнструменты.mapError(_, ошибка => "Сбой вычисления: {ошибка}")
|> ResultИнструменты.recover(_, ошибка => {
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>
}
function создатьНаблюдаемый<T>() -> Наблюдаемый<T> {
return Наблюдаемый { подписчики: [] }
}
function подписатьНаблюдаемый<T>(наблюдаемый: Наблюдаемый<T>, колбэк: (T) -> void) -> Наблюдаемый<T> {
return наблюдаемый{ подписчики: [...наблюдаемый.подписчики, колбэк] }
}
function отправитьНаблюдаемому<T>(наблюдаемый: Наблюдаемый<T>, значение: T) {
наблюдаемый.подписчики.forEach(колбэк => колбэк(значение))
}
function мапитьНаблюдатель<T, U>(преобразование: (T) -> U, колбэк: (U) -> void) -> (T) -> void {
return значение => колбэк(преобразование(значение))
}
function фильтроватьНаблюдатель<T>(условие: (T) -> bool, колбэк: (T) -> void) -> (T) -> void {
return значение => {
if условие(значение) {
колбэк(значение)
}
}
}
// Пример использования
let наблюдательКоординат = фильтроватьНаблюдатель(
событие => событие.тип == "клик",
мапитьНаблюдатель(
событие => событие.координаты,
координаты => print("Позиция клика: {координаты}")
)
)
let наблюдательДвойногоКлика = фильтроватьНаблюдатель(
событие => событие.двойнойКлик,
событие => print("Обнаружен двойной клик")
)
let потокКликов0 = создатьНаблюдаемый()
let потокКликов1 = подписатьНаблюдаемый(потокКликов0, наблюдательКоординат)
let потокКликов2 = подписатьНаблюдаемый(потокКликов1, наблюдательДвойногоКлика)
// Отправка событий
отправитьНаблюдаемому(потокКликов2, { тип: "клик", координаты: [100, 200], двойнойКлик: false })
отправитьНаблюдаемому(потокКликов2, { тип: "клик", координаты: [300, 400], двойнойКлик: true })
Продвинутые функциональные техники
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>
}
function создатьЛенивое<T>(функцияВычисления: () -> T) -> Ленивый<T> {
return Ленивый { функцияВычисления, кэш: None }
}
function вычислитьЛенивое<T>(ленивое: Ленивый<T>) -> { lazy: Ленивый<T>, value: T } {
match ленивое.кэш {
case Some(значение) => return { lazy: ленивое, value: значение }
case None => {
let вычисленноеЗначение = ленивое.функцияВычисления()
let кэшированноеЛенивое = ленивое{ кэш: Some(вычисленноеЗначение) }
return { lazy: кэшированноеЛенивое, value: вычисленноеЗначение }
}
}
}
function мапитьЛенивое<T, U>(ленивое: Ленивый<T>, преобразование: (T) -> U) -> Ленивый<U> {
return создатьЛенивое(() => {
let вычисленное = вычислитьЛенивое(ленивое)
return преобразование(вычисленное.value)
})
}
// Бесконечная последовательность
struct ЛениваяПоследовательность<T> {
генератор: (int) -> T
}
function создатьЛенивуюПоследовательность<T>(генератор: (int) -> T) -> ЛениваяПоследовательность<T> {
return ЛениваяПоследовательность { генератор }
}
function взятьИзЛенивойПоследовательности<T>(
последовательность: ЛениваяПоследовательность<T>,
количество: int
) -> Array<T> {
let mut результат: Array<T> = []
for i in 0..<количество {
результат.push(последовательность.генератор(i))
}
return результат
}
function мапитьЛенивуюПоследовательность<T, U>(
последовательность: ЛениваяПоследовательность<T>,
преобразование: (T) -> U
) -> ЛениваяПоследовательность<U> {
return создатьЛенивуюПоследовательность(индекс => преобразование(последовательность.генератор(индекс)))
}
function фильтроватьЛенивуюПоследовательность<T>(
последовательность: ЛениваяПоследовательность<T>,
условие: (T) -> bool
) -> ЛениваяПоследовательность<T> {
return создатьЛенивуюПоследовательность(индекс => {
let mut текущийИндекс = 0
let mut найденныйСчетчик = 0
while true {
let значение = последовательность.генератор(текущийИндекс)
if условие(значение) {
if найденныйСчетчик == индекс {
return значение
}
найденныйСчетчик += 1
}
текущийИндекс += 1
}
})
}
// Кэшируемое ленивое значение
let дорогоеЗначение0 = создатьЛенивое(() => "topaz".toUpperCase())
let первоеВычисление = вычислитьЛенивое(дорогоеЗначение0)
let дорогоеЗначение1 = первоеВычисление.lazy
let второеВычисление = вычислитьЛенивое(дорогоеЗначение1)
let ленивыйРазмер = мапитьЛенивое(дорогоеЗначение1, значение => значение.length)
let вычисленныйРазмер = вычислитьЛенивое(ленивыйРазмер)
print("Первое ленивое значение: {первоеВычисление.value}") // "TOPAZ"
print("Кэшированное ленивое значение: {второеВычисление.value}") // "TOPAZ"
print("Преобразованное ленивое значение: {вычисленныйРазмер.value}") // 5
// Бесконечная последовательность натуральных чисел
let натуральныеЧисла = создатьЛенивуюПоследовательность(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Квадратов}")
Функциональная библиотека Топаз обеспечивает декларативное и выразительное программирование. Используйте функции высшего порядка, монады, ленивые вычисления и многое другое для написания более элегантного и безопасного кода!