简体   繁体   中英

how capture the arguments number need in a function (currying function partial application)

I am working on the curring function and partial application, I am trying to improve the function schonfinkelize:

        function schonfinkelize(fn){
            var 
                slice = Array.prototype.slice,
                stored_args = slice.call(arguments, 1);
            return function(){
                var 
                    new_args = slice.call(arguments),
                    args = stored_args.concat(new_args);

                return fn.apply(null, args);
            }
        }

This function permit to pass as argument a function and a part of the argument of the function passed as argument (partial application) so the first time you return a function and then when you fire the function again the result.

function add(x, y, z){
    return x + y + z;
}

var val = schonfinkelize(add, 1, 2);

console.log( val(3) ) // console output--> 6

I want check inside schonfinkelize the number of arguments need to the function "add" (but it should work with every function) so I can choose when return another function or directly the result of the function "add".

bacause if I use schonfinkelize in this way:

var val2 = schonfinkelize(add, 1, 2, 3);
console.log( val2 )    // --> function
console.log( val2() )  // --> 6

I have to fire the function two time, instead a want avoid this behavior and define directly the value if the arguments are sufficient .


A possible solution could be the following:

        function schonfinkelize(fn){
            var 
                slice = Array.prototype.slice,
                stored_args = slice.call(arguments, 1);


                //* I have added this ********
                if(fn.apply(null, stored_args))
                    return fn.apply(null, stored_args);
                //****************************

            return function(){
                var 
                    new_args = slice.call(arguments),
                    args = stored_args.concat(new_args);

                return fn.apply(null, args);
            }
        }

Could be because it returns immediately the result if the fn.apply(null, stored_args) return something that is not "null" or "NaN" but I think is not really performant and then I want work with the arguments.

I don't think there is a correct way to determine number of arguments for arbitrary function. I prefer to store len in function if it is necessary, and check if it is defined, and if it is and if fn.len == stored_args.length then return function that just returns value.

function schonfinkelize(fn){
  var 
    slice = Array.prototype.slice,
    stored_args = slice.call(arguments, 1);

  if (fn.len != undefined && fn.len == stored_args.length) {
    var val = fn.apply(null, stored_args);

    return function () {
      return val;
    };
  }

  return function () {
    var 
      new_args = slice.call(arguments),
      args = stored_args.concat(new_args);

    return fn.apply(null, args);
  };
}

var f = function (a, b, c) {
  return a + b + c;
};

f.len = 3;

var g = schonfinkelize(f, 1, 2);
alert(g); // function () { var new_args = slice.call(arguments), args = stored_args.concat(new_args); return fn.apply(null, args); };
alert(g(3)); // 6

var g = schonfinkelize(f, 1, 2, 3);
alert(g); // function () { return val; };
alert(g()); // 6

As long as you put in place a requirement that the parameters defined for the function passed reflect the actually number of arguments that are to be ultimately received, you can use the .length property of the function to do the comparison of passed arguments to anticipated arguments.

function schonfinkelize(fn) {
    if (fn.length === arguments.length - 1)
        return fn.apply(this, [].slice.call(arguments, 1));

    var 
        slice = Array.prototype.slice,
        stored_args = slice.call(arguments, 1);
    return function(){
        var 
            new_args = slice.call(arguments),
            args = stored_args.concat(new_args);

        return fn.apply(null, args);
    }
}

Side note... you can avoid the .slice() if you cache the fn in a new variable, and overwrite the first argument with the this value, then use .call.apply() ...

    if (fn.length === arguments.length - 1) {
        var func = fn;
        arguments[0] = this;
        return func.call.apply(func, arguments);
    }

In strict mode browsers you could even avoid having the make the new variable since the parameters are no longer mapped to changes in the arguments . But this doesn't work in browsers that don't support strict mode .

I want propose also a personal evolution of the code but I have to said thanks to squint to has resolved the problem, simply suggest me to use the property .length . The next level it is in my opinion permit to create a partial function able to be called every time you want until you finish to fill all the arguments, I have also simplified the code:

        function schonfinkelize(fn, stored_args){
            if(fn.length == stored_args.length)
                return fn.apply(null, stored_args);

            return function(){
                var 
                    new_args = arguments[0],
                    args = stored_args.concat(new_args);

                if(fn.length == args.length)
                    return fn.apply(null, args);

                return schonfinkelize(fn, args);
            }   
        }


        function add(x, y, w, z){
            return x + y + w + z;
        }


        var 
            val  = schonfinkelize(add, [1, 2, 3, 4]),
            val2 = schonfinkelize(add, [1, 2]),
            val3 = schonfinkelize(add, [1]);


        // checking
        console.log(val);           // output --> 10 // called only 1 time
        console.log(val2([3, 4]));  // output --> 10 // called in 2 times

        val3 = val3([2]);
        val3 = val3([3]);
        console.log(val3([4]));     // output --> 10 // called 4 times!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM