← Назад к вопросам
Реализовать функцию pipe и compose
1.8 Middle🔥 81 комментариев
#JavaScript Core#Архитектура и паттерны
Условие
Напишите функции pipe и compose для композиции функций.
Требования
- pipe(f1, f2, f3)(x) эквивалентно f3(f2(f1(x))) - слева направо
- compose(f1, f2, f3)(x) эквивалентно f1(f2(f3(x))) - справа налево
- Должны работать с любым количеством функций
Примеры
const add5 = x => x + 5;
const multiply2 = x => x * 2;
const subtract3 = x => x - 3;
const piped = pipe(add5, multiply2, subtract3);
piped(10); // ((10 + 5) * 2) - 3 = 27
const composed = compose(subtract3, multiply2, add5);
composed(10); // (10 + 5) * 2 - 3 = 27
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Задача на pipe и compose — демонстрирует функциональное программирование и преобразование функций.
Что такое pipe и compose?
pipe — слева направо
- pipe(f, g, h)(x) = h(g(f(x)))
compose — справа налево
- compose(f, g, h)(x) = f(g(h(x)))
Решение 1: Базовые версии
const pipe = (...fns) => (x) =>
fns.reduce((acc, fn) => fn(acc), x);
const compose = (...fns) => (x) =>
fns.reduceRight((acc, fn) => fn(acc), x);
// Использование
const add5 = x => x + 5;
const multiply2 = x => x * 2;
const subtract3 = x => x - 3;
const piped = pipe(add5, multiply2, subtract3);
console.log(piped(10)); // ((10 + 5) * 2) - 3 = 27
const composed = compose(subtract3, multiply2, add5);
console.log(composed(10)); // ((10 + 5) * 2) - 3 = 27
Решение 2: С типизацией TypeScript
type Fn<T, R> = (arg: T) => R;
function pipe<T, R>(...fns: Fn<any, any>[]): (arg: T) => R {
return (x: T) => fns.reduce((acc, fn) => fn(acc), x);
}
function compose<T, R>(...fns: Fn<any, any>[]): (arg: T) => R {
return (x: T) => fns.reduceRight((acc, fn) => fn(acc), x);
}
// Использование с типами
const add5 = (x: number): number => x + 5;
const multiply2 = (x: number): number => x * 2;
const subtract3 = (x: number): number => x - 3;
const piped = pipe(add5, multiply2, subtract3);
const result: number = piped(10); // Type-safe!
Решение 3: С проверкой ошибок
const pipe = (...fns) => {
if (fns.length === 0) {
throw new Error("pipe requires at least one function");
}
return (x) => {
try {
return fns.reduce((acc, fn) => {
if (typeof fn !== "function") {
throw new Error("All arguments to pipe must be functions");
}
return fn(acc);
}, x);
} catch (error) {
console.error("Error in pipe:", error);
throw error;
}
};
};
const compose = (...fns) => {
if (fns.length === 0) {
throw new Error("compose requires at least one function");
}
return (x) => {
try {
return fns.reduceRight((acc, fn) => {
if (typeof fn !== "function") {
throw new Error("All arguments to compose must be functions");
}
return fn(acc);
}, x);
} catch (error) {
console.error("Error in compose:", error);
throw error;
}
};
};
Решение 4: С поддержкой асинхронных функций
const asyncPipe = (...fns) => async (x) => {
let result = x;
for (const fn of fns) {
result = await fn(result);
}
return result;
};
const asyncCompose = (...fns) => async (x) => {
let result = x;
for (let i = fns.length - 1; i >= 0; i--) {
result = await fns[i](result);
}
return result;
};
// Использование с async функциями
const fetchUser = async (id) => {
const response = await fetch(`/api/users/${id}`);
return response.json();
};
const formatName = async (user) => ({
...user,
name: user.name.toUpperCase()
});
const piped = asyncPipe(fetchUser, formatName);
const user = await piped(123);
Решение 5: Практический пример
// Функции для работы со строками
const trim = str => str.trim();
const toLowerCase = str => str.toLowerCase();
const split = separator => str => str.split(separator);
const filter = predicate => arr => arr.filter(predicate);
const join = separator => arr => arr.join(separator);
// Создаём pipeline обработки текста
const processText = pipe(
trim,
toLowerCase,
split(/\s+/),
filter(word => word.length > 2),
join(" ")
);
const result = processText(" HELLO WORLD WI ");
console.log(result); // "hello world"
Решение 6: С кэшированием промежуточных результатов
const cachedPipe = (...fns) => {
const cache = new Map();
return (x) => {
const key = JSON.stringify(x);
if (cache.has(key)) {
return cache.get(key);
}
const result = fns.reduce((acc, fn) => fn(acc), x);
cache.set(key, result);
return result;
};
};
Сравнение подходов
| Вариант | Простота | Async | Кеш | Типы | Рекомендация |
|---|---|---|---|---|---|
| Базовый | ✅✅ | Нет | Нет | Нет | ✅ Best |
| TypeScript | ✅ | Нет | Нет | ✅✅ | Production |
| С проверкой | ✅ | Нет | Нет | Нет | Defensive |
| Async | 📊 | ✅✅ | Нет | Нет | Для async |
| Практический | ✅ | Нет | Нет | Нет | Real-world |
Real-world пример: Обработка данных API
const getJSON = response => response.json();
const filterActive = users => users.filter(u => u.active);
const sortByName = users => users.sort((a, b) => a.name.localeCompare(b.name));
const extractNames = users => users.map(u => u.name);
const getActiveUserNames = pipe(
fetch,
getJSON,
filterActive,
sortByName,
extractNames
);
const names = await getActiveUserNames('/api/users');
Best Practices
- reduce/reduceRight — основа реализации
- Однострочные функции — идеальны для pipe/compose
- Типизация — использовать TypeScript
- Async support — для работы с promises
- Обработка ошибок — оборачивать в try-catch
Рекомендации для собеседования
- Начните с базовых версий (reduce/reduceRight)
- Объясните разницу между pipe и compose
- Покажите практический пример
- Добавьте обработку ошибок
- Упомяните async версии
Итог: Базовые версии просты и понятны. TypeScript версия лучше для production.