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

后端 未结 2 968
自闭症患者
自闭症患者 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:34

    Lazy evaluation solves this trivially. While we don't have that in JavaScript, we can emulate it by passing a function instead of a value:

    const foldR = f => acc => xs => xs.length
      ? f(xs[0])(() => foldR(f)(acc)(xs.slice(1)))
      : acc //   ^^^^^ "lazy"
    
    const map = f => foldR(x => acc => [f(x)].concat(acc()))([])
    const every = f => foldR(x => acc => f(x) && acc())(true)
    //                                        ^^^^^^^^ short-circuited - f(x) ? acc() : false
    
    let xs = [1, 2, 3, 4];
    console.log(map(x => x+1)(xs)); // [2, 3, 4, 5]
    console.log(every(x => x%2==0)(xs)); // false
    

    An alternative would be to use CPS, where you can easily jump to the end of the function:

    const foldL = f => acc => xs => cont => xs.length
      ? f(acc)(xs[0])(res => foldL(f)(res)(xs.slice(1))(cont))
      : cont(acc);
    
    const map = f => foldL(acc => x => cont => f(x)(res => cont(acc.concat([res]))))([]);
    //const every = f => // xs => cont =>
    //            foldL(acc => x => c => f(x)(res => c(acc && res)))(true) // (xs)(cont)
    //                                   ^^^^ eager call
    const every = f => xs => cont =>
                  foldL(acc => x => c => acc ? f(x)(c) : cont(false))(true)(xs)(cont)
    //                 call only when acc=true ^^^^      ^^^^^^^^^^^ jump out early otherwise
    let xs = [1, 2, 3, 4];
    let inc = x => cont => cont(x+1);
    map(inc)(xs)(console.log.bind(console)); // [2, 3, 4, 5]
    
    let even = x => cont => cont(x%2==0)
    every(even)(xs)(console.log.bind(console)); // false
    

提交回复
热议问题