javascript: özyinelemeli anonim işlev?


120

Diyelim ki temel bir özyinelemeli fonksiyonum var:

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

Böyle anonim bir işleve sahipsem bunu nasıl yapabilirim ...

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

Bu işlevi çağıran işlevi çağırmanın bir yolunu istiyorum ... Bir yerde (nerede olduğunu hatırlayamıyorum) çağrılan bir işlevin adını söyleyebilecek komut dosyaları gördüm, ancak hiçbirini hatırlayamıyorum şu anda bu bilgi.


Buna ihtiyaç duymanın bir nedeni var mı yoksa sadece merak mı ediyorsun? Bana öyle geliyor ki, ona bir isim vermek daha açık olur ...
rfunduk

1
@thenduks: Anonim bir işlevin kullanılmasının aynı nedenden dolayı. Sadece bazen özyineleme gereklidir.
dürtmek

5
Utanç verici bir durum arguments.calleevar ve bu işlev yararlı hiçbir şey yapmıyor. Y kombini arıyordum :P. Kahretsin, bu şeyler asla işe
yaramayacak

1
Evet, Kobi bağlantılı olduğundan, arguments.callee olmadan anonim özyinelemeli işlevler yapmak için Y gibi sabit bir nokta birleştirici kullanın.
steamer25

1
JS'deki Y birleştiricisinin bir örneği için w3future.com/weblog/stories/2002/02/22/… adresine bakın .
steamer25

Yanıtlar:


145

Sen edebilirsiniz Eğer bir değer ve bir "işlev bildirimi" ifadesi olarak işlev oluştururken bile, işlevini bir ad verin. Diğer bir deyişle:

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

yığın üfleyen özyinelemeli bir işlevdir. Şimdi, muhtemelen bunu genel olarak yapmak istemeyebilirsiniz çünkü Javascript'in çeşitli uygulamalarında bazı tuhaf sorunlar vardır. ( not - bu oldukça eski bir yorum; Kangax'ın blog gönderisinde açıklanan sorunların bazıları / çoğu / tümü daha modern tarayıcılarda düzeltilebilir.)

Böyle bir isim verdiğinizde, isim işlevin dışında görünmez (yani olması gerekmiyor; bu tuhaflıklardan biri). Lisp'teki "letrec" gibi.

Buna gelince arguments.callee, buna "katı" modda izin verilmez ve genellikle kötü bir şey olarak kabul edilir, çünkü bazı optimizasyonları zorlaştırır. Aynı zamanda beklenenden çok daha yavaştır.

edit - Kendini çağırabilen "anonim" bir işlevin etkisine sahip olmak istiyorsanız, bunun gibi bir şey yapabilirsiniz (işlevi bir geri arama veya benzeri bir şey olarak geçirdiğinizi varsayarak):

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

Bunun yaptığı şey, güzel, güvenli, IE'de kırılmamış işlev bildirim deyimiyle bir işlevi tanımlamak ve adı genel ad alanını kirletmeyecek yerel bir işlev oluşturmaktır. Sarmalayıcı (gerçekten anonim) işlevi yalnızca o yerel işlevi döndürür.


ES5 sctrict ile küresel ad alanını başka bir şekilde kirletmekten kaçınabilir miyiz (henüz ES5'i derinlemesine okumadım)?
Incognito

@pointy lütfen bu arayışa bakar mısınız? stackoverflow.com/questions/27473450/…
Gladson Robinson

Sanırım (() => { call_recursively_self_here() })()kendini yinelemeli olarak kullanmak ve aramak mümkün değil , değil mi? Ona bir isim vermeliyim.
Qwerty

1
@Qwerty, cevabımdaki son örnek gibi bir şey yapabilirsin. Ok işlevini bir sarmalayıcı işlevindeki yerel bir değişkene bağlayın, böylece ok işleviniz değişken adıyla kendisine başvurabilir. Sarmalayıcı daha sonra değişkeni döndürür (ok işlevine atıfta bulunur).
Pointy

1
@Pointy belki bazı bilgisayar korsanları uygulamayı bulabilir;)
Kamil Kiełczewski

31

İnsanlar yorumlarda Y birleştirici hakkında konuştu, ancak kimse cevap olarak yazmadı.

Y birleştirici javascript'te şu şekilde tanımlanabilir: (bağlantı için steamer25 sayesinde)

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

Ve anonim işlevinizi geçmek istediğinizde:

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

Bu çözüm hakkında dikkat edilmesi gereken en önemli şey, onu kullanmamanız gerektiğidir.


16
"Bu çözümle ilgili unutulmaması gereken en önemli şey, onu kullanmamanız gerektiğidir." Neden?
nyuszika7h

7
Hızlı olmayacak. Aslında kullanmak çirkin (kavramsal olarak güzel olsa da!). İşlevinize bir etiket veya değişken adı vermek zorunda kalmazsınız (ve bunun neden bir sorun olduğunu anlamıyorum), ancak Y'ye geçirilen dış işleve parametre olarak yine de bir ad veriyorsunuz. tüm bu sıkıntılardan geçerek bir şey kazanın.
zem

Bu işlevin yığın güvenli olmadığını söylemeyi unutmayın. Yalnızca birkaç bin kez döngü yapmak, yığın taşmasına neden olur.
Teşekkürler

Merhaba, .apply (null, arguments) bana çirkin göründüğü için biraz "daha temiz" bir değişiklik öneririm: var Y = function (gen) {return (function (f) {return f (f);} (function (f) {dönüş gen (işlev (x) {dönüş f (f) (x);});})); } Veya eşdeğer olarak ((işlev (x) {dönüş y} eşittir (x => y))) ok gösterimini kullanarak (geçerli js kodu): var Y = gen => (f => f (f)) (f = > gen (x => f (f) (x)))
myfirstAnswer

23

U birleştirici

Bir işlevi argüman olarak kendisine ileterek, bir işlev adı yerine parametresini kullanarak tekrar edebilir! Yani verilen fonksiyonU , işleve (kendisine) bağlanacak en az bir parametresi olmalıdır.

Aşağıdaki örnekte çıkış koşulumuz yok, bu nedenle bir yığın taşması gerçekleşene kadar süresiz döngü yapacağız.

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

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

Sonsuz özyinelemeyi çeşitli teknikler kullanarak durdurabiliriz. Burada, bir girdi bekleyen başka bir anonim işlevi döndürmek için anonim işlevimizi yazacağım ; bu durumda, bir numara. Bir sayı verildiğinde, 0'dan büyükse, yinelemeye devam ederiz, aksi takdirde 0 döndürür.

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

Burada hemen belli olmayan şey, fonksiyonumuz, Ubirleştirici kullanılarak kendisine ilk uygulandığında , ilk girdiyi bekleyen bir fonksiyon döndürmesidir. Buna bir isim verirsek, lambdalar (anonim fonksiyonlar) kullanarak özyinelemeli fonksiyonları etkin bir şekilde inşa edebiliriz.

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

Yalnızca bu doğrudan özyineleme değildir - kendi adını kullanarak kendisini çağıran bir işlev. Bizim tanımımız countDown, vücudunun içinde kendisine atıfta bulunmuyor ve yine de yineleme mümkündür

// 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 birleştirici kullanılarak mevcut bir işlevden öz referans nasıl kaldırılır

Burada, kendisine bir referans kullanan özyinelemeli bir işlevi nasıl alacağınızı ve bunu öz referans yerine U birleştiriciyi kullanan bir işleve nasıl değiştireceğinizi göstereceğim.

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

Şimdi U birleştiriciyi kullanarak iç referansı değiştirmek için factorial

const U = f => f (f)

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

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

Temel değiştirme modeli budur. Zihinsel bir not alın, sonraki bölümde benzer bir strateji kullanacağız.

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

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

Y birleştirici

ilgili: U ve Y birleştiricileri bir ayna benzetmesi kullanarak açıkladı

Önceki bölümde, kendi kendine referans özyinelemesinin, U birleştirici kullanılarak adlandırılmış bir işleve dayanmayan özyinelemeli bir işleve nasıl dönüştürüleceğini gördük. İşlevi her zaman ilk argüman olarak kendisine aktarmayı hatırlamak zorunda kalmanın biraz sıkıntısı var. Y-birleştirici, U-birleştiriciye dayanıyor ve bu sıkıcı kısmı ortadan kaldırıyor. Bu iyi bir şey çünkü karmaşıklığı kaldırmak / azaltmak, işlevleri yapmamızın birincil nedenidir

İlk olarak, kendi Y-birleştiricimizi türetelim

// 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)))

Şimdi kullanımının U-birleştiriciye kıyasla nasıl olduğunu göreceğiz. Dikkat edin, tekrarlamak yerine U (f)basitçe arayabilirizf ()

const U = f => f (f)

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

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

Şimdi countDownkullanarak programı göstereceğim Y- programların neredeyse aynı olduğunu göreceksiniz ancak Y birleştirici işleri biraz daha temiz tutuyor

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

Ve şimdi göreceğiz factorialyanı

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

Gördüğünüz gibi, fözyineleme mekanizmasının kendisi haline geliyor. Tekrarlamak için buna sıradan bir işlev diyoruz. Bunu farklı argümanlarla birçok kez arayabiliriz ve sonuç yine de doğru olacaktır. Sıradan bir fonksiyon parametresi olduğu için, onu recuraşağıdaki gibi istediğimiz gibi adlandırabiliriz -

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


1'den fazla parametreye sahip U ve Y birleştirici

Yukarıdaki örneklerde, hesaplamamızın "durumunu" takip etmek için nasıl bir argüman oluşturup geçebileceğimizi gördük. Peki ya ek durumu takip etmemiz gerekirse?

Biz olabilir bir Array falan gibi bileşik verileri kullanmak ...

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

Ancak bu kötü çünkü iç durumu açığa çıkarıyor (sayaçlar ave b). Sadece arayabilseydik güzel olurdufibonacci (7) cevabı almak için .

Curried fonksiyonlar hakkında bildiklerimizi (tekli (1-paramter) fonksiyon dizileri) kullanarak, tanımımızı değiştirmek zorunda kalmadan hedefimize kolayca ulaşabiliriz. Y , birleşik verilere veya gelişmiş dil özelliklerine veya güvenmek .

fibonacciAşağıda yakından tanımına bakın. Hemen uyguluyoruz 0ve 1bunlara bağlı olan ave bsırasıyla. Şimdi fibonacci, bağlanacak olan son argümanın sağlanmasını bekliyor x. Yinelediğimizde, çağırmalıyız f (a) (b) (x)(değil f (a,b,x)) çünkü fonksiyonumuz curried formdadır.

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


Bu tür bir kalıp, her türden işlevi tanımlamak için yararlı olabilir. Kullandığımız tanımlanan iki işlevleri göreceksiniz Aşağıda Ybir bağdaştırıcının ( rangeve reduce) ve bir türevini reduce, map.

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 ]


TAMAMEN ANONİM OMG

Burada saf fonksiyonlarla çalıştığımız için, adlandırılmış herhangi bir fonksiyonu tanımının yerine koyabiliriz. Fibonacci'yi aldığımızda ve adlandırılmış fonksiyonları ifadeleriyle değiştirdiğimizde ne olduğunu izleyin

/* 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

Ve işte var - fibonacci (7)anonim işlevlerden başka hiçbir şey kullanmadan özyinelemeli olarak hesaplanır


14

Bunun yerine "anonim nesne" kullanmak en basit yöntem olabilir:

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

Küresel alanınız tamamen kirlenmemiş. Oldukça basit. Ve nesnenin küresel olmayan durumundan kolayca yararlanabilirsiniz.

Sözdizimini daha kısa hale getirmek için ES6 nesne yöntemlerini de kullanabilirsiniz.

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

13

Bunu satır içi bir işlev olarak yapmam. İyi zevkin sınırlarını zorluyor ve size gerçekten hiçbir şey kazandırmıyor.

Gerçekten mecbursanız, arguments.calleeFabrizio'nun cevabındaki gibi var. Ancak bu genellikle tavsiye edilmez olarak kabul edilir ve ECMAScript Beşinci Sürüm'ün 'katı modu'nda izin verilmez. ECMA 3 ve katı olmayan mod ortadan kalkmasa da, katı modda çalışmak daha olası dil optimizasyonları vaat ediyor.

Ayrıca adlandırılmış bir satır içi işlev de kullanılabilir:

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

Bununla birlikte, IE'nin JScript'i onlara bazı kötü şeyler yaptığından, adlandırılmış satır içi işlev ifadelerinden de kaçınılması en iyisidir. Yukarıdaki örnekte, fooIE'deki üst kapsamı yanlış bir şekilde kirletmektedir ve üst, içeride görülenden fooayrı bir örnektir .foofoo

Bunu satır içi anonim bir işleve koymanın amacı nedir? Sadece ana kapsamı kirletmekten kaçınmak istiyorsanız, elbette ilk örneğinizi başka bir kendi kendini çağıran anonim işlevin (ad alanı) içine gizleyebilirsiniz. nothingÖzyineleme etrafında her defasında yeni bir kopya oluşturmanız gerçekten gerekiyor mu? İki basit karşılıklı özyinelemeli işlev içeren bir ad alanıyla daha iyi durumda olabilirsiniz.


Kabul ediyorum, adlandırılmış bir işlev arguments.callee'den yalnızca ekmascript katı modu için değil, aynı zamanda bir optimizasyon meselesi için de uygundur çünkü her özyinelemede aranan uca bir referans alması gerekir (ve bu muhtemelen yürütme hızını azaltabilir) )

Şiirsel için +1, "pushing against the boundaries of good taste"- (ve iyi bilgi).
Peter Ajtai

Burada kirlilik gerçekten önemliyse basit bir ön / son eke ne dersiniz? Küresel kapsamda olmadığı düşünüldüğünde (işlev top lvl olsa bile, zaten tüm kodunu saran anonim bir işleve sahip olmalıdır) gibi bir adın recur_fooüst kapsamdaki bir işlevle çakışması (veya hasta olması ) gerçekten olası değildir. -Kullanılmış) .
gblazex

Çok ilginç - jsfiddle.net/hck2A - IE, söylediğiniz gibi bu durumda ebeveyni kirletiyor. Bunu asla fark etmedim.
Peter Ajtai

1
@Peter: kangax.github.com/nfe (özellikle 'JScript hataları') bu konuda bilmek isteyeceğinizden daha fazlası için. Sonunda IE9'da düzeltildi (ancak yalnızca IE9 Standartları Modunda).
bobince

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

34
Umarım bu (teknik olarak doğru) yanıta oy veren herkes şu sorunların farkına arguments.calleevarır: katı modda ve ES5'te buna izin verilmiyor.
Pointy

Kabul edilmedi, arguments.callee ES5'te kullanımdan kaldırıldı
Jaime Rodriguez

NodeJS'de çalışır. Sabit bir ortamda öngörülebilir bir şekilde çalıştığı sürece ES5 hakkında daha az umursayamazdım.
Angad

1
Bu bir saatli bomba. Yukarıdaki yorumun önerdiği gibi, "sabit" ortam denen böyle bir şey yoktur. Binlerce nedenden herhangi biri nedeniyle neredeyse her zaman yükseltme yaparsınız.
sampathsris

6

Şunun gibi bir şey yapabilirsiniz:

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

veya sizin durumunuzda:

(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İlk önce bir varifade ile beyan etmekle yapabilirsiniz . Bunun sorunun kurallarını ihlal edip etmediğini Dunno, ancak şimdi sahip olduğunuz gibi, varifade olmadan ECMAScript 5 katı modunda bir hata alırsınız.
Tim Down

İlk yorumum varanahtar kelimeyi içeriyordu , ancak bu kodu bir kez test ettiğimde hata veriyordu, çünkü kendi kendini çağıran bir bloğun içindeki bir değişkeni gerçekten açıklayamazsınız ve benim yaklaşımım tanımlanmamış bir değişkenin otomatik bildirimine ve dolayısıyla @ Pointy's çözüm daha doğrudur. Ama yine de Fabrizio Calderan cevabına oy verdim;)
ArtBIT

Evet, yapmak (var recur = function() {...})();artık bir atama ifadesi (atanan değeri döndüren) değil de bir ifade olduğundan işe yaramayacak. Onun var recur; (recur = function() {...})();yerine öneriyordum .
Tim Down

3

Böyle bir anonim işlevi ilan ettiğinizde:

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

Bir işlev ifadesi olarak kabul edilir ve isteğe bağlı bir adı vardır (bunu kendi içinden çağırmak için kullanabilirsiniz. Ancak bir işlev ifadesi olduğu için (ve bir ifade değil) anonim kalır (ancak arayabileceğiniz bir adı vardır). bu işlev kendisini çağırabilir:

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

"anonim kalır" - hayır öyle değil. Anonim bir işlevin bir adı yoktur. Bunun foomevcut bağlam içinde beyan edilmediğini anlıyorum , ancak bu aşağı yukarı alakasız. Adı olan bir işlev, anonim değil , hala adlandırılmış bir işlevdir .
Teşekkürler

3

Neden işlevi işlevin kendisine aktarmıyorsunuz?

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

3

Bazı durumlarda anonim işlevlere güvenmeniz gerekir. Verilen, özyinelemeli bir mapişlevdir:

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

Lütfen mapdizinin yapısını değiştirmemesi gerektiğini unutmayın . Yani akümülatörün accaçığa çıkması gerekmez. mapÖrneğin başka bir fonksiyona sarabiliriz :

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

  return next([])(xs);
}

Ancak bu çözüm oldukça ayrıntılı. Az tahmin edilen Ubirleştiriciyi kullanalım :

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

Kısa, değil mi? Uson derece basittir, ancak yinelemeli çağrının biraz karmaşık sum(...)hale gelmesi dezavantajına sahiptir: olur h(h)(...)- hepsi bu.


2

Cevabın hala gerekli olup olmadığından emin değilim, ancak bu, function.bind kullanılarak oluşturulan delegeler kullanılarak da yapılabilir:

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

Bu, adlandırılmış işlevleri veya arguments.callee'yi içermez.


1

Bobince'nin yazdığı gibi, işlevinize bir ad verin.

Ama tahmin ediyorum ki siz de bir başlangıç ​​değeri vermek ve sonunda işlevinizi durdurmak istiyorsunuz!

var initialValue = ...

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

çalışma jsFiddle örneği (eğlence için veri + = veri kullanır)



1
+1, Bu çok faydalı bir cevap ve bunun için daha fazla oy almalısınız, ancak anonim değil.
Incognito

Açıkça bobince yazdıklarını okumadım: However named inline function expressions are also best avoided.. Ama OP de noktayı kaçırıyor ... :)
gblazex

@Galamb - Okudum. Katı modda ve ES5'te izin verilmemesi, bir üst kapsamı kirletmek ve fazladan örnekler oluşturmakla aynı şey değildir.
Peter Ajtai

1

Bir dizge oluşturan bir nesnede yukarı çıkmak için tek satırlık bir anonim işleve ihtiyacım vardı (veya daha doğrusu, istedim) ve bunu şu şekilde ele aldım:

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

bu, 'Kök: foo: bar: baz: ...' gibi bir dize üretir.


1

ES2015 ile sözdizimi ile biraz oynayabilir ve varsayılan parametreleri ve thunk'ları kötüye kullanabiliriz. İkincisi, herhangi bir argüman içermeyen işlevlerdir:

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

Lütfen bunun varsayılan değeri olarak fanonim işlevli bir parametre olduğunu unutmayın (x, y, n) => n === 0 ? x : f(y, x + y, n - 1). Bu çağrı ftarafından ne zaman çağrıldığında applyT, varsayılan değerin kullanılması için bağımsız değişkenler olmadan gerçekleşmesi gerekir. Varsayılan değer bir işlevdir ve dolayısıyla fkendisini özyinelemeli olarak çağırabilen adlandırılmış bir işlevdir.


0

Adlandırılmış işlev veya argümanlar içermeyen başka bir cevap. 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: anonim bir işlevi yerel bir parametreye bağlayın ve ardından işlevi yerel parametre aracılığıyla çağırın, ancak aynı zamanda işlevi özyineleme için kendisine iletin.
englebart

0

Bu, farklı isimler ve biraz değiştirilmiş bir giriş ile jforjs yanıtının yeniden çalışmasıdır.

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

İlk özyinelemeyi silmeye gerek yoktu. Kendini bir referans olarak alan işlev, OOP'nin ilksel sızıntısına geri döner.


0

Bu, @ zem'in ok işlevli yanıtının bir versiyonudur.

Sen kullanabilirsiniz UveyaY bir bağdaştırıcının. Y birleştirici, kullanımı en basit olanıdır.

U birleştirici, bununla işlevi geçmeye devam etmelisiniz: const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))

Y birleştirici, bununla işlevi geçmeye devam etmek zorunda değilsiniz: const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))


0

Yine, rosetta-kod bağlantısını kullanan başka bir Y-birleştirici çözümü (sanırım birisi daha önce stackOverflow'da bir yerde bağlantıdan bahsetmişti.

Oklar anonim işlevler içindir bana daha okunaklı:

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

-1

Bu her yerde çalışmayabilir, ancak arguments.calleemevcut işleve başvurmak için kullanabilirsiniz .

Böylece, faktöriyel şu şekilde yapılabilir:

var fac = function(x) { 
    if (x == 1) return x;
    else return x * arguments.callee(x-1);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.