Home > Mobile >  Using filter() to implemnt curriable() in javascript is fial , however, uisng map() is work, why?
Using filter() to implemnt curriable() in javascript is fial , however, uisng map() is work, why?

Time:02-19

I'm trying to implement the curry() with placeholder support, that is curriable. curriable provides a high performance and small footprint curry method. The version1 is the working code is using map in the recursion function. However, I tried to use filter to filter the placeholder in the version2! Using filter is good for most cases, but fail with case curriedJoin(_,_,3,4)(1,_)(2,5) withe reason Uncaught TypeError: curriedJoin(...)(...) is not a function, can any one tell me why?

version 1

/**
 * @param { (...args: any[]) => any } fn
 * @returns { (...args: any[]) => any }
 */
// function curry(fn) {
//   // your code here
// }

function curry(func) {
  return function curried(...args) {
    const complete = args.length >= func.length && !args.slice(0, func.length).includes(curry.placeholder);
    
    if(complete) {
      args.length=3
      args.sort((a,b)=>a-b)
      return func.apply(this, args)
    }
    return function(...newArgs) {
      // replace placeholders in args with values from newArgs using map
      const res = args.map(arg => arg === curry.placeholder && newArgs.length ? newArgs.shift() : arg);
      return curried(...res, ...newArgs);
    }
  }
}
const  join = (a, b, c) => {
   return `${a}_${b}_${c}`
}

curry.placeholder = Symbol()

const curriedJoin = curry(join)
const _ = curry.placeholder

console.log(curriedJoin(1, 2, 3)) // '1_2_3'
console.log(curriedJoin(_, 2)(1, 3)) // '1_2_3'
console.log(curriedJoin(_, _, _)(1)(_, 3)(2)) // '1_2_3'
console.log(curriedJoin(_,_,3,4)(1,_)(2,5))// '1_2_3'

Version2

/**
 * @param { (...args: any[]) => any } fn
 * @returns { (...args: any[]) => any }
 */
// function curry(fn) {
//   // your code here
// }

function curry(func) {
  return function curried(...args) {
    const complete = args.length >= func.length && !args.slice(0, func.length).includes(curry.placeholder);
    
    if(complete) {
      args.length=3
      args.sort((a,b)=>a-b)
       return func.apply(this, args)
    }
    return function(...newArgs) {
      // replace placeholders in args with values from newArgs
      const res = [...args].filter(element=> element!== curry.placeholder);
      return curried(...res, ...newArgs);
    }
  }
}
const  join = (a, b, c) => {
   return `${a}_${b}_${c}`
}

curry.placeholder = Symbol()

const curriedJoin = curry(join)
const _ = curry.placeholder

console.log(curriedJoin(1, 2, 3)) // '1_2_3'
console.log(curriedJoin(_, 2)(1, 3)) // '1_2_3'
console.log(curriedJoin(_, _, _)(1)(_, 3)(2)) // '1_2_3'
console.log(curriedJoin(_,_,3,4)(1,_)(2,5)) //Fail, because "Uncaught TypeError: curriedJoin(...)(...) is not a function"

CodePudding user response:

If you add

console.log(curriedJoin(_,_,3,4)(1,_));

just before the failing line, you'll see that curriedJoin(_, _, 3, 4)(1, _) has already resolved to the string "1_3_4" ...which is not a function. Hence the error. The two versions behave quite differently (logging the various arg lists during processing is instructive.) The problem with using the "filter and append" approach is that it's not putting the args in the correct order. For the failing example, after one iteration, the 3rd placeholder ends up at the end of the arg list, beyond where the "complete" test is checking, instead of in the 2nd position where it belongs. What happens when filtering is that (_, _, 3, 4)(1, _) becomes (3,4,1,_), but the func only expects 3 good args, so this looks "complete" - oops! When using the "map" algorithm, (_, _, 3, 4)(1, _) becomes (1, _, 3, 4) which is clearly incomplete, so it goes into another recursion level as desired.

CodePudding user response:

Use of .sort involves comparison of argument values and will not apply arguments with respect to their applied positions. Use of args.length = 3 will support only functions with 3 parameters.

Adding parameter n = f.length supports currying of fixed-arity functions as well as variadic functions. Adding curry.merge with sound inductive reasoning simplifies merging arguments and reduces complexity of curry.

function curry(f, n = f.length) {
  return function loop(...args) {
    const a = args.slice(0, n)
    return a.some(v => v === curry.placeholder)
      ? (...b) => loop(...curry.merge(a, b))
      : f(...a)
  }
}
curry.placeholder = Symbol("curry.placeholder")
curry.merge = (l, r) =>
  l.length == 0 && r.length == 0
    ? []
  : l.length == 0
    ? r
  : r.length == 0
    ? l
  : l[0] === curry.placeholder
    ? [r[0], ...curry.merge(l.slice(1), r.slice(1))]
  : [l[0], ...curry.merge(l.slice(1), r)]

const _ = curry.placeholder

const add = curry((a,b,c) => a   b   c)
console.log(add(1, 2, 3))             // 6
console.log(add(_, 2)(1, 3))          // 6
console.log(add(_, _, _)(1)(_, 3)(2)) // 6
console.log(add(_,_,3,4)(1,_)(2,5))   // 6

const foo = curry(console.log, 3)
foo(1, 2, 3)             // 1 2 3
foo(_, 2)(1, 3)          // 1 2 3
foo(_, _, _)(1)(_, 3)(2) // 1 2 3
foo(_,_,3,4)(1,_)(2,5)   // 1 2 3

Maybe a better name for curry is partial as the technique you are using here is more closely related to Partial Application.

  • Related