How to implement a more general reduce function to allow early exit?

后端 未结 2 966
自闭症患者
自闭症患者 2021-02-04 21:11

reduce (aka foldL in FP) is the most general iterative higher order function in Javascript. You can implement, for instance, map or

2条回答
  •  温柔的废话
    2021-02-04 21:49

    It has turned out that the generalization of reduce can be quite easily achieved once you have become accustomed to CPS:

    const foldL = f => acc => xs => xs.length
     ? f(acc)(xs[0])(xss => foldL(f)(xss)(xs.slice(1)))
     : acc;
    
    const map = f => foldL(acc => x => cont => cont(acc.concat([f(x)])))([]);
    const filter = pred => foldL(acc => x => cont => cont(pred(x) ? acc.concat([x]) : acc))([]);
    const every = pred => foldL(acc => x => cont => pred(x) ? cont(true) : false)(true);
    const some = pred => foldL(acc => x => cont => pred(x) ? true : cont(false))(false);
    const takeN = n => foldL(acc => x => cont => acc.length < n ? cont(acc.concat([x])) : acc)([]);
    
    const comp = f => g => x => f(g(x));
    const not = f => x => !f(x);
    const inc = x => ++x;
    const odd = x => x & 1;
    const even = not(odd);
    const lt3 = x => x < 3;
    const eq3 = x => x === 3;
    const sqr = x => x * x;
    const xs = [1, 2, 3, 4, 5];
    
    map(inc)(xs); // [2, 3, 4, 5, 6]
    filter(even)(xs); // [2, 4]
    every(lt3)(xs); // false
    some(lt3)(xs); // true
    takeN(3)(xs); // [1, 2, 3]
    
    // we can compose transforming functions as usual
    map(comp(inc)(sqr))(xs); // [2, 5, 10, 17, 26]
    
    // and the reducing functions as well
    comp(map(inc))(filter(even))(xs); // [3, 5]
    comp(takeN(2))(filter(odd))(xs); // [1, 3]
    

    As you can see this isn't really pure CPS, but mixed with Direct Style. This has the great benefit that foldL and the usual transforming functions have to carry around no additional continuation argument, but maintain their normal signatures.

    I use CPS functions only in parts of the code, where they are irreplaceable to achieve the desired behavior. CPS is an extremely powerful construct and you'd always use the least expressive constructs that you can.

    comp(takeN(2))(filter(odd))(xs) illustrates one of the weaknesses of the implementation (there will be probably others). The composition of the reducing functions does not take place at the level of the array elements. Thus an intermediate array is required ([1, 3, 5]) before the final result can be computed ([1, 3]). But that's the matter of transducers...

提交回复
热议问题