Javascript карри - PullRequest
       19

Javascript карри

8 голосов
/ 11 марта 2011

Я пытаюсь создать функцию карри , которая может быть применена к любой функции и возвращает другую, с одним из примененных аргументов.Свойства, которые я хочу иметь:

  1. Если функция имеет только один аргумент, функция карри должна возвращать значение: f (a);curry (f, x) = f (x);
  2. Если функция имеет много аргументов, currey должен повторно запустить карри-функцию: g (a1, a2, .., aN);карри (g, x) = g2 (a2, .., aN): g2 (a2, .. aN) = g (x, a2, ..., aN)
  3. Длина свойства карри функции должнаработать "по мере необходимости" g.length = N => curry (g, x) .length = N-1

В Prototype Framework есть некоторые реализации карри и обсуждение в одном блоге.Но эта реализация не очень хороша, потому что она плохо работает с функциями только с одним аргументом (1), а также возвращает атрибут функции length с длиной 0 (3).

Для первого свойства легкореализация:

 function curry(f,x) {
    if (f.length == 1) return f(x);
    ...
 }

Но я не знаю, как работать с 3-м правилом, то есть функция может быть построена как внутренняя функция, поскольку будет вложенная Лексическая среда и она сможет использовать f:

function curry(f,x) {
   return function() { ... }
}

но в этом случае я больше не смогу явно устанавливать параметры.С другой стороны, функция может быть построена с помощью оператора «новая функция», что-то вроде этого:

 function curry(f,x) {
    var args = [];
    for (var i=1; i<f.length; i++) {
       args.push('a'+i);
    }
    var sa = args.join();
    return new Function(sa,"return f(x,"+sa+")");
 }

Но в этом случае f и x не будут связаны, потому что анонимная функция будет создана в Global Lexical Environment.

Итак, вопросы:

  1. есть ли способ явно установить количество параметров при создании функции с помощью ключевого слова функции?
  2. есть ли способ задания среды функции, созданной с помощьюоператор 'new Function'?
  3. у нас есть способ решить мою проблему любым другим способом?

Ответы [ 4 ]

6 голосов
/ 11 марта 2011

Способ, которым реализует библиотека Functional , заключается в том, чтобы передать параметры, передаваемые функции "curry ()", в качестве параметров first , которые необходимо передать.Результат функции операции «карри» будет затем принимать любые дополнительные параметры, переданные при вызове, и добавлять их в end списка аргументов.Он не беспокоится о длине списка аргументов, потому что в JavaScript это вообще не исправлено, поэтому в этом нет никакого смысла.

Таким образом:

var curry = myFunction.curry("Tuesday", x + y);

Итак, вызов:

curry(100, true);

будет аналогичен вызову:

myFunction("Tuesday", x + y, 100, true);

Функционал имеет еще одну функцию под названием "partual ()", которая позволяет более контролируемую подстановку параметров.Когда вы вызываете "partal () ", вы передаете фиктивный аргумент (« _ »), чтобы указать, где« дыры »находятся в списке аргументов:

var partialFunc = myFunction.partial("Tuesday", _, 100, true, _, "banana");

Эти два параметра« _ »означают, чторезультирующий «absoluteFunc» должен отбросить первые два передаваемых ему аргумента в эти слоты в списке аргументов:

partialFunc(x + y, "Texas");

, таким образом, похож на вызов:

myFunction("Tuesday", x + y, 100, true, "Texas", "banana");

Я от всей души рекомендую получить эту библиотекуи глядя на код.Это на удивление лаконично и ясно.

Еще одна вещь: важно отметить, что, поскольку JavaScript не является ленивым языком оценки, это не действительно то же самое, что операция "карри"на ленивом функциональном языке, таком как Haskell.Различие состоит в том, что аргументы в «время карри» оцениваются и, следовательно, как бы «готовятся» к результату.На ленивом языке все по-другому.

3 голосов
/ 11 марта 2011
function curry(fn, args) {
  // no need to var these, they are scoped via argument list - we overwrite them
  // convert the arguments to a real array:
  args = [].slice.apply(arguments);
  // first argument is a function:
  fn = args.shift();
  return function() {
    // get internal args
    var iArgs = [].slice.apply(arguments);
    // apply curried arguments, then our arguments:
    return fn.apply(this, args.concat(iArgs));
  }
}

function add(a,b) { return a+b; }
var add2 = curry(add, 2);
alert(add2(5)); //7

var hello = curry(add, "Hello ");
alert(hello("World!"));
2 голосов
/ 11 марта 2011

В течение многих лет я использовал прототип функции для карри, который выглядит следующим образом:

Function.prototype.curry = function curry() {
    var fn = this, args = Array.prototype.slice.call(arguments);
    return function curryed() {
        return fn.apply(this, args.concat(Array.prototype.slice.call(arguments)));
    };
};

Возможно, это подойдет и вашим потребностям.

Вы просто используете это так:

function fn1(arg1,arg2) { /*...*/ }

var fn1Curried = fn1.curry('whatever'); //sets arg1

Будет работать с любым количеством аргументов.

0 голосов
/ 27 декабря 2012
function curry(func) {
    var initial_args = [].slice.apply(arguments, [1]);
    var func_args_length = func.length;

    function curried(args) {
        if (args.length >= func_args_length) {
            return func.apply(null, args);
        }

        return function () {
            return curried(args.concat([].slice.apply(arguments)));
        };
    }

    return curried(initial_args);
}
...