(() => { // combinations :: Int -> [a] -> [[a]] const combinations = (n, xs) => { const cmb_ = (n, xs) => { if (n < 1) return [ [] ]; if (xs.length === 0) return []; const h = xs[0], tail = xs.slice(1); return cmb_(n - 1, tail) .map(cons(h)) .concat(cmb_(n, tail)); }; return memoized(cmb_)(n, xs); } // GENERIC FUNCTIONS ------------------------------------------------------ // 2 or more arguments // curry :: Function -> Function const curry = (f, ...args) => { const go = xs => xs.length >= f.length ? (f.apply(null, xs)) : function () { return go(xs.concat(Array.from(arguments))); }; return go([].slice.call(args, 1)); }; // cons :: a -> [a] -> [a] const cons = curry((x, xs) => [x].concat(xs)); // enumFromTo :: Int -> Int -> [Int] const enumFromTo = (m, n) => Array.from({ length: Math.floor(n - m) + 1 }, (_, i) => m + i); // Derive a memoized version of a function // memoized :: Function -> Function const memoized = f => { let m = {}; return function (x) { let args = [].slice.call(arguments), strKey = args.join('-'), v = m[strKey]; return ( (v === undefined) && (m[strKey] = v = f.apply(null, args)), v ); } }; // show :: a -> String const show = (...x) => JSON.stringify.apply( null, x.length > 1 ? [x[0], null, x[1]] : x ); return show( memoized(combinations)(3, enumFromTo(0, 4)) ); })();