Конечно, есть.
1-й вариант: преобразователь
const mapReduce = map => reduce => (acc, x) =>
reduce(acc, map(x));
const filterReduce = filter => reduce => (acc, x) =>
filter(x)
? reduce(acc, x)
: acc;
const transduce = (...ts) => xs =>
xs.reduce(ts.reduce(comp, id) (concat), []);
const comp = (f, g) =>
x => f(g(x));
const id = x => x;
const concat = (xs, ys) =>
xs.concat(ys);
const sqr = n => n * n;
const isOdd = n => n & 1 === 1;
const log = console.log;
// the upper code is usually library code
// so you don't have to deal with its complexity but only with its API
const tx = filterReduce(isOdd),
ty = mapReduce(sqr);
const r = transduce(tx, ty) ([1,2,3,4,5]); // filter/map in same iteration
log(r);
2-й вариант: голая рекурсия с эффектом оптимизации хвостового вызова
const loop = f => {
let acc = f();
while (acc && acc.type === tailRec)
acc = f(...acc.args);
return acc;
};
const tailRec = (...args) =>
({type: tailRec, args});
const comp = (f, g) => x =>
f(g(x));
const sqr = n => n * n;
const isOdd = n => n & 1 === 1;
const log = console.log;
// the upper code is usually library code
// so you don't have to deal with its complexity but only with its API
const r = loop((xs = [1,2,3,4,5], acc = [], i = 0) => {
if (i === xs.length)
return acc;
else
return tailRec( // filter/map in same iteration
xs,
isOdd(xs[i]) ? acc.concat(sqr(xs[i])) : acc,
i + 1);
});
log(r);
Я бы сказал, что преобразователь предназначен для обычных и более простых итераций, тогда как рекурсия подходит для более сложных, например, когда вам нужно короткое замыкание (преждевременный выход).