web-dev-qa-db-ja.com

javascript:再帰的な匿名関数?

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

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();
})();

この関数を呼び出した関数を呼び出す方法が必要です...呼び出された関数の名前を伝えることができるスクリプトをどこかで(どこで覚えているかわかりません)見ましたが、今その情報。

112
Incognito

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

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

スタックを吹き飛ばす再帰関数です。さて、あなたは おそらくしない Javascriptのさまざまな実装にはいくつかの奇妙な問題があるため、これをしたくないかもしれませんnote—これはかなり古いコメントです。Kangaxのブログ投稿で説明されている問題の一部/多く/すべては、最新のブラウザーで修正される可能性があります。)

そのような名前を付けると、その名前は関数の外では見えません(まあ、そうではないはずです;それは奇妙なことの1つです)。 LISPの「letrec」のようなものです。

はどうかと言うと arguments.callee、これは「厳格」モードでは禁止されており、一般に悪いことと見なされます。これは、最適化を困難にするためです。また、予想よりはるかに遅いです。

edit—自分自身を呼び出すことができる「匿名」関数の効果が必要な場合は、次のようなことができます(関数をコールバックとして渡すか、そんな感じ):

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

それは、素敵で安全なIEで壊れていない関数declarationステートメントで関数を定義し、名前のないローカル関数を作成することですグローバル名前空間を汚染します。ラッパー(本当に匿名の)関数は、そのローカル関数を返すだけです。

136
Pointy

人々はY Combinatorについてコメントで話しましたが、答えとしてそれを書いた人はいませんでした。

Y Combinatorは、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();
  }
})());

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

30
zem

Uコンビネータ

Uコンビネータは関数を受け取り、それを自分自身に適用します。したがって、指定する関数には、関数にバインドするパラメーター(少なくとも1つ)が必要です。

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

_const U = f => f (f)

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

さまざまな手法を使用して、無限再帰を停止できます。ここでは、入力を待機しているanother無名関数を返す匿名関数を作成します。この場合、いくつかの番号。数値が指定されたときに、それが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_

これだけがdirect再帰ではありません-独自の名前を使用して自分自身を呼び出す関数です。 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コンビネータ

関連: と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 Combinatorを使用すると少しきれいになります。

_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コンビネーター

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

私たちはcould配列または何かのような複合データを使用します...

_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_

しかし、内部状態(カウンターaおよびb)を公開しているため、これは悪いことです。 fibonacci (7)を呼び出して、必要な答えを得ることができればいいですね。

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

fibonacciのすぐ下の定義を見てください。 abにそれぞれバインドされている_0_と_1_をすぐに適用しています。フィボナッチは、最後の引数が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_

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

_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 ]_

IT'S ALL ANONYMOUS 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)は、匿名関数のみを使用して再帰的に計算しました

18
user633183

代わりに「匿名オブジェクト」を使用するのが最も簡単な場合があります。

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

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

14
svidgen

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

本当に必要な場合は、arguments.calleeファブリツィオの答えのように。ただし、これは一般的にお勧めできないと見なされ、ECMAScript第5版の「厳格モード」では許可されていません。 ECMA 3と非厳密モードはなくなりませんが、厳密モードでの作業はより多くの言語最適化を約束します。

名前付きインライン関数も使用できます。

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

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

これをインライン匿名関数に入れる目的は何ですか?親スコープの汚染を避けたい場合は、もちろん、別の自己呼び出し匿名関数(名前空間)内で最初の例を非表示にすることができます。本当に再帰のたびにnothingの新しいコピーを作成する必要がありますか? 2つの単純な相互再帰関数を含む名前空間を使用した方がよい場合があります。

13
bobince
(function(data){
    var recursive = arguments.callee;
    data = data+1;
    var nothing = function() {
        recursive(data)
    }
    nothing();
})();
11
fcalderan

次のようなことができます:

(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);
6
ArtBIT

関数を関数自​​体に渡さないのはなぜですか?

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

このような匿名関数を宣言する場合:

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

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

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

特定の状況では、匿名関数に依存する必要があります。再帰的な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)(...)になります-それだけです。

3
user6445533

答えがまだ必要かどうかはわかりませんが、これは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は含まれません。

2
Nitij

Bobinceが書いたように、単に関数に名前を付けてください。

ただし、初期値を渡して、関数を最終的に停止することも必要だと思います!

var initialValue = ...

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

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


1
Peter Ajtai

文字列を構築するオブジェクトを上にたどり、次のように処理するために、1行の匿名関数が必要でした(むしろ必要でした)。

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

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

1
radio_babylon

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)をデフォルト値として持つパラメーターです。 fapplyTによって呼び出される場合、この呼び出しは引数なしで実行する必要があるため、デフォルト値が使用されます。デフォルト値は関数であるため、fは名前付き関数であり、それ自体を再帰的に呼び出すことができます。

1
user6445533

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

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
0
jforjs

これは、異なる名前とわずかに変更されたエントリを使用した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
englebart

rosetta-code linkを使用した別のYコンビネーターソリューション.

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

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

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

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

Uコンビネーター、これを使用して関数を渡す必要があります:const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))

Yコンビネータ。これにより、関数を渡すことを続ける必要はありません:const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))

0
Ricardo Freitas