javascript:再帰的な匿名関数?


120

基本的な再帰関数があるとしましょう:

function recur(data) {
    data = data+1;
    var nothing = function() {
        recur(data);
    }
    nothing();
}

次のような匿名関数がある場合、どうすればこれを実行できますか?

(function(data){
    data = data+1;
    var nothing = function() {
        //Something here that calls the function?
    }
    nothing();
})();

この関数を呼び出した関数を呼び出す方法が欲しい...呼び出された関数の名前を教えてくれるスクリプトがどこか(どこにあるか思い出せない)を見たことがありますが、どれも思い出せませんその情報今。


これが必要な理由はありますか、それとも単に興味があるのですか?単純に名前を付けた方が
わかりやすいと思い

1
@thenduks:匿名関数を使用するのと同じ理由で。ちょうどそれが時々再帰が必要です。
突く

5
それは残念だarguments.calleeが存在し、このfunctnioは何も有効ではありません。Yコンビ ネーターを探していました:P。くそー、そのようなものは決して役立つことはありません...
Kobi

1
はい、Kobiがリンクしているので、Yなどの固定小数点コンビネーターを使用して、arguments.calleeなしで匿名の再帰関数を実行します。
蒸し器25

1
JSのYコンビネーターの例については、w3future.com / weblog / stories / 2002/02/22 /…を参照してください。
蒸し器

回答:


145

「関数宣言」ステートメントではなく値として関数を作成している場合でも、関数に名前を付けることができます。言い換えると:

(function foo() { foo(); })();

スタックブロー再帰関数です。さて、そうは言っても、JavaScriptのさまざまな実装にはいくつかの奇妙な問題があるので、一般的にはこれ望まないかもしれません。( -これはかなり古いコメントです。Kangaxのブログ投稿で説明されている問題の一部/多く/すべては、より新しいブラウザーで修正される可能性があります。)

そのような名前を付けると、その名前は関数の外には表示されません(まあ、それは想定されていません。それは奇妙さの1つです)。Lispの「letrec」のようなものです。

についてはarguments.callee、これは「strict」モードでは許可されておらず、一部の最適化が困難になるため、一般に悪いことと見なされています。また、予想よりもはるかに遅いです。

編集 —自分自身を呼び出すことができる「無名」関数の効果を持たせたい場合は、次のようなことを行うことができます(関数をコールバックとして渡すなど)。

asyncThingWithCallback(params, (function() {
  function recursive() {
    if (timeToStop())
      return whatever();
    recursive(moreWork);
  }
  return recursive;
})());

それが行うのは、IEが壊れていない、安全で見栄えのよい関数宣言ステートメントを使用して関数を定義し、名前がグローバル名前空間を汚染しないローカル関数を作成することです。ラッパー(真に匿名)関数は、そのローカル関数を返すだけです。


ES5 sctrictを使用して別の方法でグローバル名前空間を汚染することを回避できますか(まだES5の詳細を読んでいません)。
インコグニート

@pointyこのクエストをご覧ください。stackoverflow.com/questions/27473450/...
Gladsonロビンソン

自分(() => { call_recursively_self_here() })()自身を再帰的に使用して呼び出すことは不可能だと思いますよね?名前をつけなきゃ。
Qwerty、2016

1
@Qwertyよく私の答えの最後の例のようなことをすることができます。アロー関数をラッパー関数のローカル変数にバインドして、アロー関数が変数名でそれ自体を参照できるようにします。その後、ラッパーは変数(矢印関数を参照)を返します。
先のとがった

1
@Pointy多分一部のハッカーはアプリケーションを見つけるでしょう;)
KamilKiełczewski19年

31

人々はコメントでYコンビネーターについて話しましたが、誰も答えとしてそれを書きませんでした。

Yコンビネータは、JavaScriptで次のように定義できます(リンクのSteamer25に感謝)

var Y = function (gen) {
  return (function(f) {
    return f(f);
  }(function(f) {
    return gen(function() {
      return f(f).apply(null, arguments);
    });
  }));
}

そして無名関数を渡したい場合:

(Y(function(recur) {
  return function(data) {
    data = data+1;
    var nothing = function() {
      recur(data);
    }
    nothing();
  }
})());

このソリューションについて注意する最も重要なことは、使用しないことです。


16
「このソリューションについて注意すべき最も重要なことは、それを使用してはならないということです。」どうして?
nyuszika7h

7
速くはありません。実際に使うのは醜いです(概念的には美しいのですが!)。関数にタグまたは変数名を付ける必要はありませんが(それが問題になる理由はわかりません)、Yに渡される外部関数へのパラメーターとして名前を付けます。したがって、このすべてのトラブルを通過することで何かを得ます。
11

この関数はスタックセーフではないことを忘れないでください。数千回だけループすると、スタックオーバーフローが発生します。
ありがとう

こんにちは。.apply(null、arguments)は私には醜いので、少し「よりクリーンな」変更を提案します。var Y = function(gen){return(function(f){return f(f);}(function(f) {return gen(function(x){return f(f)(x);});})); または、同等に((function(x){return y} equals(x => y)))を使用して矢印表記(有効なjsコード):var Y = gen =>(f => f(f))(f = > gen(x => f(f)(x)))
myfirstAnswer 2018年

23

Uコンビネーター

関数を引数としてそれ自体に渡すことにより、関数は名前の代わりにそのパラメーターを使用して再帰できます!したがって、指定さUれた関数には、関数(それ自体)にバインドされる少なくとも1つのパラメーターが必要です。

以下の例では、終了条件がないため、スタックオーバーフローが発生するまで無限にループします

const U = f => f (f) // call function f with itself as an argument

U (f => (console.log ('stack overflow imminent!'), U (f)))

さまざまな手法を使用して、無限再帰を停止できます。ここでは、入力を待機している別の匿名関数を返す匿名関数を記述します。この場合、いくつかの数。数値が指定された場合、0より大きい場合は繰り返しが続行され、それ以外の場合は0が返されます。

const log = x => (console.log (x), x)

const U = f => f (f)

// when our function is applied to itself, we get the inner function back
U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)
// returns: (x => x > 0 ? U (f) (log (x - 1)) : 0)
// where f is a reference to our outer function

// watch when we apply an argument to this function, eg 5
U (f => x => x > 0 ? U (f) (log (x - 1)) : 0) (5)
// 4 3 2 1 0

ここですぐに明らかにならないのは、関数がUコンビネーターを使用して最初に適用されると、最初の入力を待つ関数を返すことです。これに名前を付けると、ラムダ(無名関数)を使用して再帰関数を効率的に構築できます

const log = x => (console.log (x), x)

const U = f => f (f)

const countDown = U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)

countDown (5)
// 4 3 2 1 0

countDown (3)
// 2 1 0

これだけが直接再帰ではありません。独自の名前を使用して自分自身を呼び出す関数です。私たちの定義はcountDown自分の体の中でそれ自体を参照しておらず、それでも再帰が可能です

// direct recursion references itself by name
const loop = (params) => {
  if (condition)
    return someValue
  else
    // loop references itself to recur...
    return loop (adjustedParams)
}

// U combinator does not need a named reference
// no reference to `countDown` inside countDown's definition
const countDown = U (f => x => x > 0 ? U (f) (log (x - 1)) : 0)

Uコンビネーターを使用して既存の関数から自己参照を削除する方法

ここでは、それ自体への参照を使用する再帰関数を取得し、それをU結合子を使用する関数に変更して、自己参照の代わりにする方法を示します。

const factorial = x =>
  x === 0 ? 1 : x * factorial (x - 1)
  
console.log (factorial (5)) // 120

Uコンビネータを使用して、内部参照を factorial

const U = f => f (f)

const factorial = U (f => x =>
  x === 0 ? 1 : x * U (f) (x - 1))

console.log (factorial (5)) // 120

基本的な交換パターンはこちらです。メンタルなメモを作ってください。次のセクションでは同様の戦略を使用します

// self reference recursion
const foo =         x => ...   foo (nextX) ...

// remove self reference with U combinator
const foo = U (f => x => ... U (f) (nextX) ...)

Yコンビネーター

関連:ミラーアナロジーを使用して説明されたUおよびYコンビネーター

前のセクションでは、Uコンビネーターを使用して、名前付き関数に依存しない自己参照再帰を再帰関数に変換する方法を説明しました。最初の引数として常に自分自身に関数を渡すことを覚えておかなければならないことで、少し面倒があります。さて、YコンビネーターはUコンビネーターに基づいて構築され、その面倒なビットを削除します。複雑さを取り除く/減らすことが私たちが関数を作る主な理由なので、これは良いことです

最初に、独自のYコンビネータを導出しましょう

// standard definition
const Y = f => f (Y (f))

// prevent immediate infinite recursion in applicative order language (JS)
const Y = f => f (x => Y (f) (x))

// remove reference to self using U combinator
const Y = U (h => f => f (x => U (h) (f) (x)))

次に、U-combinatorと比較して使用方法を確認します。繰り返しますが、U (f)単に呼び出す代わりにf ()

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

Y (f => (console.log ('stack overflow imminent!'),  f ()))

ここで、countDownプログラムを使用してデモンストレーションしYます。プログラムはほとんど同じですが、Yコンビネータにより、少しクリーンになっています。

const log = x => (console.log (x), x)

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const countDown = Y (f => x => x > 0 ? f (log (x - 1)) : 0)

countDown (5)
// 4 3 2 1 0

countDown (3)
// 2 1 0

そして今、我々は表示されますfactorialだけでなく

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const factorial = Y (f => x =>
  x === 0 ? 1 :  x * f (x - 1))

console.log (factorial (5)) // 120

ご覧のとおりf、再帰自体のメカニズムになります。繰り返しますが、通常の関数のように呼び出します。異なる引数を使用して複数回呼び出すことができ、結果は正しいままです。そしてそれは通常の関数パラメーターなので、recur以下のように好きな名前を付けることができます-

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (recur => n =>
  n < 2 ? n : recur (n - 1) +  (n - 2))

console.log (fibonacci (10)) // 55


複数のパラメーターを持つUおよびYコンビネーター

上記の例では、計算の「状態」を追跡するためにループして引数を渡す方法を示しました。しかし、追加の状態を追跡する必要がある場合はどうでしょうか。

我々は可能性が配列か何かのような化合物のデータを使用して...

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (f => ([a, b, x]) =>
  x === 0 ? a : f ([b, a + b, x - 1]))

// starting with 0 and 1, generate the 7th number in the sequence
console.log (fibonacci ([0, 1, 7])) 
// 0 1 1 2 3 5 8 13

しかし、これは内部状態(カウンターab)を公開しているので悪いです。必要なfibonacci (7)答えを得るために電話をかけるだけでいいのですが。

カリー関数(単項(1パラメーター)関数のシーケンス)について知っていることを使用してY、複合データの定義を変更したり、複合データや高度な言語機能に依存したりすることなく、目的を簡単に達成できます。

fibonacci以下の定義をよく見てください。私たちは、すぐに適用している01に結合しているab、それぞれ。現在、フィボナッチは、バインドされる最後の引数が提供されるのを単に待っていますx。再帰するときは、関数がカリー化されているため、f (a) (b) (x)(ではなくf (a,b,x))を呼び出す必要があります。

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const fibonacci = Y (f => a => b => x =>
  x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1)

console.log (fibonacci (7)) 
// 0 1 1 2 3 5 8 13


この種のパターンは、あらゆる種類の関数を定義するのに役立ちます。以下は、我々は2つの関数が使用して定義されて表示されますYコンビネータを(rangereduce)およびその誘導体reducemap

const U = f => f (f)

const Y = U (h => f => f (x => U (h) (f) (x)))

const range = Y (f => acc => min => max =>
  min > max ? acc : f ([...acc, min]) (min + 1) (max)) ([])

const reduce = Y (f => g => y => ([x,...xs]) =>
  x === undefined ? y : f (g) (g (y) (x)) (xs))
  
const map = f =>
  reduce (ys => x => [...ys, f (x)]) ([])
  
const add = x => y => x + y

const sq = x => x * x

console.log (range (-2) (2))
// [ -2, -1, 0, 1, 2 ]

console.log (reduce (add) (0) ([1,2,3,4]))
// 10

console.log (map (sq) ([1,2,3,4]))
// [ 1, 4, 9, 16 ]


それはすべて匿名のOMG

ここでは純粋な関数を使用しているので、名前付き関数をその定義の代わりに使用できます。フィボナッチを取り、名前付き関数をその式で置き換えるとどうなるかを見てください

/* const U = f => f (f)
 *
 * const Y = U (h => f => f (x => U (h) (f) (x)))
 *
 * const fibonacci = Y (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1)
 *
 */

/*
 * given fibonacci (7)
 *
 * replace fibonacci with its definition
 * Y (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
 *
 * replace Y with its definition
 * U (h => f => f (x => U (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
//
 * replace U with its definition
 * (f => f (f)) U (h => f => f (x => U (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
 */

let result =
  (f => f (f)) (h => f => f (x => h (h) (f) (x))) (f => a => b => x => x === 0 ? a : f (b) (a + b) (x - 1)) (0) (1) (7)
  
console.log (result) // 13

そして、あなたはそれを持っています– fibonacci (7)匿名関数だけを使って再帰的に計算されます


14

代わりに「匿名オブジェクト」を使用するのが最も簡単かもしれません:

({
  do: function() {
    console.log("don't run this ...");
    this.do();
  }
}).do();

あなたのグローバル空間は完全に汚染されていません。とても簡単です。また、オブジェクトの非グローバル状態を簡単に利用できます。

ES6オブジェクトのメソッドを使用して、構文をより簡潔にすることもできます。

({
  do() {
    console.log("don't run this ...");
    this.do();
  }
}).do();

13

インライン関数としてこれを行うことはありません。それは、美味しさの境界を押し広げており、実際には何も得られません。

あなたが本当にしなければならない場合arguments.callee、ファブリツィオの答えのようにあります。ただし、これは一般に推奨されないと考えられており、ECMAScript Fifth Editionの「ストリクトモード」では許可されていません。ECMA 3とnon-strict-modeがなくなるわけではありませんが、strictモードでの作業は、より可能な言語最適化を約束します。

名前付きのインライン関数を使うこともできます:

(function foo(data){
    data++;
    var nothing = function() {
        foo(data);
    }
    nothing();
})();

ただし、名前付きのインライン関数式も回避するのが最善です。IEのJScriptがいくつかの悪いことを行うためです。上記の例ではfoo、誤ってIE内の親スコープを汚染し、親はfooに別個のインスタンスでfoo見られる内部foo

これをインラインの無名関数に入れる目的は何ですか?親スコープを汚染しないようにする場合は、もちろん、最初の例を別のself-calling-anonymous-function(名前空間)内に隠すことができます。本当にnothing再帰のたびに新しいコピーを作成する必要がありますか?2つの単純な相互再帰関数を含む名前空間の方が良いかもしれません。


名前付き関数は引数よりも適しています.calleeはecmascript strictモードだけでなく、再帰ごとに呼び出し先への参照を取得する必要があるため、最適化の問題にも適しています(これにより実行速度が低下する可能性があります) )

詩の場合は+ 1、"pushing against the boundaries of good taste"-(まあ、良い情報です)。
Peter Ajtai

ここで汚染が本当に懸念される場合、単純な前置/後置はどうですか?それがグローバルスコープにないことを考えると(関数が最上位レベルであっても、コード全体をラップする無名関数が既にあるはずです)、名前recur_fooが親スコープの関数と衝突することはほとんどありません(または病気になることはありません) -中古) 。
gblazex 2010年

非常に興味深い-jsfiddle.net/hck2A-IEは、あなたが言ったように、この場合、親を汚染します。気づかなかった。
Peter Ajtai

1
@Peter:kangax.github.com/nfe(特に「JScriptバグ」)。IE9でようやく修正されました(ただしIE9標準モードでのみ)。
ボビンス

10
(function(data){
    var recursive = arguments.callee;
    data = data+1;
    var nothing = function() {
        recursive(data)
    }
    nothing();
})();

34
この(技術的に正しい)回答に投票するすべてarguments.calleeの人が、strictモードとES5では許可されていない問題を理解することを願っています。
先のとがっ

反対票が投じられ、arguments.calleeはES5で廃止されます
Jaime Rodriguez

NodeJSで動作します。ES5が固定された環境で予測可能な方法で機能する限り、ES5を気にする必要はありません。
アンガッド、2015

1
これは時限爆弾です。上記のコメントが示唆するように、「固定」環境と呼ばれるものはありません。アップグレードする理由は何千もあるため、ほとんどの場合アップグレードします。
sampathsris 2018

6

あなたは次のようなことをすることができます:

(foo = function() { foo(); })()

またはあなたの場合:

(recur = function(data){
    data = data+1;
    var nothing = function() {
        if (data > 100) return; // put recursion limit
        recur(data);
    }
    nothing();
})(/* put data init value here */ 0);

recur最初にvarステートメントを宣言することで実現できます。Dunnoはそれが質問の規則に違反しているかどうかはわかりませんが、今のように、varステートメントがないとECMAScript 5のstrictモードでエラーが発生します。
Tim Down

最初のコメントにはvarキーワードが含まれていましたが、このコードをテストすると、自己呼び出しブロック内で変数を実際に宣言できないため、エラーがスローされました。私のアプローチは、未定義の変数の自動宣言に依存しているため、@ Pointy'sソリューションはより正確です。しかし、私はまだファブリツィオ
カルデランの

はい、(var recur = function() {...})();割り当て式(割り当てられた値を返す)ではなくステートメントになっているため、機能しません。var recur; (recur = function() {...})();代わりに私は提案していました。
Tim Down

3

次のように無名関数を宣言すると、

(function () {
    // Pass
}());

これは関数式と見なされ、オプションの名前(それ自体から呼び出すために使用できます)を持っています。ただし、これは関数式(ステートメントではない)であるため、匿名のままです(ただし、呼び出し可能な名前があります)。この関数はそれ自体を呼び出すことができます:

(function foo () {
    foo();
}());
foo //-> undefined

「匿名のまま」 –いいえ、そうではありません。無名関数には名前がありません。それfooが現在のコンテキスト内で宣言されないことを理解していますが、多かれ少なかれ無関係です。名前の付いた関数は、匿名ではなく名前の付いた関数です。
ありがとう

3

関数をfunctio自体に渡しませんか?

    var functionCaller = function(thisCaller, data) {
        data = data + 1;
        var nothing = function() {
            thisCaller(thisCaller, data);
        };
        nothing();
    };
    functionCaller(functionCaller, data);

3

特定の状況では、匿名関数に依存する必要があります。与えられたのは再帰map関数です:

const map = f => acc => ([head, ...tail]) => head === undefined 
 ? acc
 : map (f) ([...acc, f(head)]) (tail);

const sqr = x => x * x;
const xs = [1,2,3,4,5];

console.log(map(sqr) ([0]) (xs)); // [0] modifies the structure of the array

map配列の構造を変更してはならないことに注意してください。したがって、アキュムレータをacc公開する必要はありません。mapたとえば、別の関数にラップできます。

const map = f => xs => {
  let next = acc => ([head, ...tail]) => head === undefined
   ? acc
   : map ([...acc, f(head)]) (tail);

  return next([])(xs);
}

しかし、このソリューションはかなり冗長です。過小評価されたUコンビネータを使用してみましょう:

const U = f => f(f);

const map = f => U(h => acc => ([head, ...tail]) => head === undefined 
 ? acc
 : h(h)([...acc, f(head)])(tail))([]);

const sqr = x => x * x;
const xs = [1,2,3,4,5];

console.log(map(sqr) (xs));

簡潔ですよね?完全にU単純ですが、再帰呼び出しが少し難読化されるという欠点があります:にsum(...)なるh(h)(...)-それだけです。


2

答えがまだ必要かどうかはわかりませんが、function.bindを使用して作成されたデリゲートを使用して行うこともできます。

    var x = ((function () {
        return this.bind(this, arguments[0])();
    }).bind(function (n) {
        if (n != 1) {
            return n * this.bind(this, (n - 1))();
        }
        else {
            return 1;
        }
    }))(5);

    console.log(x);

これには、名前付き関数やarguments.calleeは含まれません。


1

bobinceが書いたように、単に関数に名前を付けます。

しかし、私はあなたも初期値を渡して、最終的に関数を停止したいと思っていると思います!

var initialValue = ...

(function recurse(data){
    data++;
    var nothing = function() {
        recurse(data);
    }
    if ( ... stop condition ... )
        { ... display result, etc. ... }
    else
        nothing();
}(initialValue));

動作するjsFiddleの例(楽しみのためにデータ+ =データを使用)



1
+1、これは非常に便利な回答であり、より多くの賛成票を獲得する必要がありますが、匿名ではありません。
シークレットモードで

あなたは明らかにボビンスが書いたものを読みませんでした:However named inline function expressions are also best avoided.。しかし、OPも要点を逃しています... :)
gblazex

@Galamb-私はそれを読んだ。厳密モードおよびES5で許可されないことは、親スコープを汚染し、追加のインスタンスを作成することと同じではありません。
Peter Ajtai

1

私は、文字列を構築するオブジェクトを上に向かって歩くための1行の無名関数が必要である(または望んでいた)ので、次のように処理しました。

var cmTitle = 'Root' + (function cmCatRecurse(cmCat){return (cmCat == root) ? '' : cmCatRecurse(cmCat.parent) + ' : ' + cmCat.getDisplayName();})(cmCurrentCat);

'Root:foo:bar:baz:...'のような文字列を生成します


1

ES2015では、構文を試したり、デフォルトのパラメーターやサンクを悪用したりすることができます。後者は引数のない単なる関数です:

const applyT = thunk => thunk();

const fib = n => applyT(
  (f = (x, y, n) => n === 0 ? x : f(y, x + y, n - 1)) => f(0, 1, n)
);

console.log(fib(10)); // 55

// Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55...

fは、(x, y, n) => n === 0 ? x : f(y, x + y, n - 1)デフォルト値として無名関数を持つパラメータであることに注意してください。この呼び出しfによって呼び出された場合はapplyT、引数なしで実行する必要があるため、デフォルト値が使用されます。デフォルト値は関数であるためf、名前付き関数であり、それ自体を再帰的に呼び出すことができます。


0

名前付き関数または引数を含まない別の答え.callee

var sum = (function(foo,n){
  return n + foo(foo,n-1);
})(function(foo,n){
     if(n>1){
         return n + foo(foo,n-1)
     }else{
         return n;
     }
},5); //function takes two argument one is function and another is 5

console.log(sum) //output : 15

nice:匿名関数をローカルパラメーターにバインドし、ローカルパラメーターを介して関数を呼び出しますが、再帰のために関数をそれ自体に渡します。
englebart

0

これは、さまざまな名前とわずかに変更されたエントリを含むjforjs回答のリワークです。

// function takes two argument: first is recursive function and second is input
var sum = (function(capturedRecurser,n){
  return capturedRecurser(capturedRecurser, n);
})(function(thisFunction,n){
     if(n>1){
         return n + thisFunction(thisFunction,n-1)
     }else{
         return n;
     }
},5); 

console.log(sum) //output : 15

最初の再帰を展開する必要はありませんでした。参照としてそれ自体を受け取る関数は、OOPの原始的なにじみに戻ります。


0

これは、矢印関数を使用した@zemの回答のバージョンです。

UまたはYコンビネータを使用できます。Yコンビネータが最も簡単に使用できます。

U コンビネータ、これであなたは関数を渡し続ける必要があります: const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))

Y Combinator、これを使用すると、関数を渡し続ける必要はありません。 const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))


0

ロゼッタコードリンクを使用した、さらに別のY結合器ソリューション(誰かが以前にリンクをstackOverflowのどこかで言及したと思います。

矢印は、私にとって読みやすい匿名関数用です。

var Y = f => (x => x(x))(y => f(x => y(y)(x)));

-1

これはどこでもarguments.callee機能するわけではありませんが、現在の関数を参照するために使用できます。

したがって、階乗はこうして行うことができます:

var fac = function(x) { 
    if (x == 1) return x;
    else return x * arguments.callee(x-1);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.