8

引数の1つを適用して、任意の関数に適用して別の関数を返すことができるカレー関数を作成しようとしています。私が欲しいプロパティ:

  1. 関数に引数が1つしかない場合、curry関数は値を返す必要があります。f(a); カレー(f、x)= f(x);
  2. 関数に多くの引数がある場合、curreyはcurried関数を再実行する必要があります: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つのブログで議論されています。ただし、この実装は、引数が1つしかない関数(1)ではうまく機能せず、関数の'length'属性が0(3)を返すため、適切ではありません。

最初のプロパティには、簡単な実装があります。

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

しかし、3番目のルールを操作する方法がわかりません。つまり、ネストされた字句環境があり、fを使用できるため、関数を内部関数として構成できます。

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

ただし、この場合、パラメーターを明示的に設定することはできなくなります。一方、関数は「newFunction」ステートメントで作成できます。smthは次のようになります。

 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はバインドされません。

だから質問:

  1. functionキーワードで関数を作成するときにパラメータカウントを明示的に設定する方法はありますか?
  2. 'new Function'ステートメントで作成された関数の環境を設定する方法はありますか?
  3. 他の方法で私の問題を解決する方法はありますか?
4

4 に答える 4

6

機能ライブラリが実装する方法は、「c​​urry()」に渡されたパラメータを最初に渡されるパラメータとして取得することです。「curry」操作の関数の結果は、呼び出されたときに渡された追加のパラメーターを受け取り、引数リストの最後に追加します。引数リストの長さについてはまったく心配しません。これは、JavaScriptでは一般的に固定されたものではないため、実際には意味がありません。

したがって:

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

だから呼び出す:

curry(100, true);

呼び出しのようになります:

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

Functionalには、パラメータのより制御された置換を可能にする「partial()」と呼ばれる別の関数があります。「partial()」を呼び出すときは、ダミーの引数( "_")を渡して、引数リストのどこに「穴」があるかを示します。

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

これらの2つの「_」パラメーターは、結果の「partialFunc」が、渡された最初の2つの引数を引数リストのそれらのスロットにドロップする必要があることを意味します。

partialFunc(x + y, "Texas");

したがって、次のように呼び出すのと同じです。

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

そのライブラリを入手して、関連するコードを確認することを心からお勧めします。驚くほど簡潔で明確です。

もう1つ重要なのは、JavaScriptは遅延評価言語ではないため、 Haskellのような遅延関数型言語での「カレー」操作と実際には同じではないということです。違いは、「カレー時」の引数が評価されるため、結果に「調理」されたものであるということです。怠惰な言語では、物事は異なります。

于 2011-03-11T13:34:00.457 に答える
3
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!"));
于 2011-03-11T13:39:35.030 に答える
2

何年もの間、私は次のようなカレーの関数プロトタイプを使用してきました。

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

これは、任意の数の引数で機能します。

于 2011-03-11T14:05:43.210 に答える
0
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);
}
于 2012-12-26T20:08:32.193 に答える