जावास्क्रिप्ट: पुनरावर्ती अनाम फ़ंक्शन?


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

मैं इस फ़ंक्शन को कॉल करने का एक तरीका चाहूंगा ... मैंने स्क्रिप्ट्स को कहीं देखा है (मुझे यह याद नहीं है कि कहां है) जो आपको कॉल किए गए फ़ंक्शन का नाम बता सकती है, लेकिन मैं किसी को भी याद नहीं कर सकता हूं वह जानकारी अभी।


क्या कोई कारण है जो आपको इसकी आवश्यकता है या आप सिर्फ उत्सुक हैं? मुझे लगता है कि यह सिर्फ एक नाम देना स्पष्ट होगा ...
rfunduk

1
@thenduks: उसी कारण से कि कोई अनाम फ़ंक्शन का उपयोग क्यों करेगा। बस, कभी-कभी पुनरावृत्ति आवश्यक है।
प्रहार

5
यह एक शर्म की बात arguments.calleeहै, और यह फंक्शंस कुछ भी उपयोगी नहीं है। मैं वाई कॉम्बिनेटर को देख रहा था :P। अरे, वह सामान कभी उपयोगी नहीं होगा ...
कोबी

1
हाँ, कोबी से जुड़े के रूप में, एक निश्चित बिंदु कॉम्बिनेटर का उपयोग करें जैसे Y बिना तर्क के अनाम पुनरावर्ती कार्यों को करने के लिए।
स्टीमर 25

1
जेएस में वाई कॉम्बिनेटर के एक उदाहरण के लिए w3future.com/weblog/stories/2002/02/22/… देखें ।
स्टीमर 25

जवाबों:


145

आप फ़ंक्शन को एक नाम दे सकते हैं, तब भी जब आप फ़ंक्शन को एक मूल्य के रूप में बना रहे हैं और "फ़ंक्शन घोषणा" बयान नहीं। दूसरे शब्दों में:

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

स्टैक-ब्लोइंग पुनरावर्ती कार्य है। अब, उस ने कहा, आप शायद सामान्य रूप से ऐसा नहीं करना चाहते हैं क्योंकि जावास्क्रिप्ट के विभिन्न कार्यान्वयन के साथ कुछ अजीब समस्याएं हैं। ( नोट करें - यह एक काफी पुरानी टिप्पणी है; कंगैक्स के ब्लॉग पोस्ट में वर्णित कुछ / कई / सभी समस्याएँ अधिक ब्राउज़र ब्राउज़रों में ठीक की जा सकती हैं।)

जब आप ऐसा नाम देते हैं, तो फ़ंक्शन के बाहर नाम दिखाई नहीं देता है (ठीक है, यह माना नहीं जाता है; यह अजीबता में से एक है)। यह लिस्प में "लेटरेक" जैसा है।

के रूप में arguments.callee, कि "सख्त" मोड में अस्वीकृत है और आम तौर पर एक बुरी चीज माना जाता है, क्योंकि यह कुछ अनुकूलन को कठिन बनाता है। यह भी उम्मीद की तुलना में बहुत धीमी है।

संपादित करें - यदि आप एक "अनाम" फ़ंक्शन का प्रभाव चाहते हैं जो स्वयं को कॉल कर सकता है, तो आप ऐसा कुछ कर सकते हैं (यह मानते हुए कि आप फ़ंक्शन को कॉलबैक के रूप में पारित कर रहे हैं या ऐसा कुछ):

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

जो ऐसा करता है वह एक फ़ंक्शन को एक अच्छा, सुरक्षित, नहीं-टूटा-इन-आईई फ़ंक्शन घोषणा विवरण के साथ परिभाषित करता है , एक स्थानीय फ़ंक्शन बनाता है जिसका नाम वैश्विक नामस्थान को प्रदूषित नहीं करेगा। रैपर (वास्तव में अनाम) फ़ंक्शन केवल उस स्थानीय फ़ंक्शन को लौटाता है।


क्या हम ईएस 5 sctrict के साथ वैश्विक नाम स्थान को प्रदूषित करने से बच सकते हैं (मैंने अभी तक ईएस 5 में गहनता से नहीं पढ़ा है)?
गुप्त

@ आप इस खोज को देख सकते हैं। stackoverflow.com/questions/27473450/…
ग्लैडसन रॉबिन्सन

मुझे लगता है कि इसका उपयोग (() => { call_recursively_self_here() })()और पुनरावर्ती रूप से कॉल करना संभव नहीं है , है ना? मुझे इसे एक नाम देना चाहिए।
क्वर्टी

1
@ क्वर्टी अच्छी तरह से आप मेरे जवाब में अंतिम उदाहरण की तरह कुछ कर सकते हैं। एक रैपर फंक्शन में एरो फंक्शन को एक स्थानीय वैरिएबल से बांधें ताकि आपका एरो फंक्शन वैरिएबल नाम के साथ खुद को संदर्भित कर सके। रैपर तब चर (जो तीर फ़ंक्शन को संदर्भित करता है) को वापस करेगा।
नुकीले

1
@ पॉइन्टी शायद कुछ हैकर्स को एप्लीकेशन
लगेगी

31

लोगों ने टिप्पणियों में वाई कॉम्बिनेटर के बारे में बात की, लेकिन किसी ने इसे उत्तर के रूप में नहीं लिखा।

Y कॉम्बिनेटर को जावास्क्रिप्ट में इस प्रकार परिभाषित किया जा सकता है: (लिंक के लिए स्टीमर 25 से धन्यवाद)

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 को दिया गया है। इस सारी परेशानी से गुजरकर कुछ भी हासिल करो।
'22

इस फ़ंक्शन का उल्लेख करना न भूलें, यह सुरक्षित नहीं है। सिर्फ एक-दो हजार बार लूपिंग करने से स्टैक ओवरफ्लो हो जाएगा।
आप

नमस्ते, मैं थोड़ा "क्लीनर" संशोधन का प्रस्ताव करूंगा।। के बाद से (अशक्त, तर्क) मुझे बदसूरत लगता है: var Y = function (gen) {return (फंक्शन (f) {return f (f);} (फंक्शन) {रिटर्न जीन (फंक्शन (x) {रिटर्न एफ (एफ) (एक्स);});}); } या समकक्ष (फ़ंक्शन (x) {वापसी y} बराबर (x => y)) तीर संकेतन (मान्य js कोड) का उपयोग करके: var Y = gen => (f => f (f)) (f =) > gen (x => f (f) (x))
myfirstAnswer

23

यू कोम्बिनेटर

एक फ़ंक्शन को एक तर्क के रूप में पारित करके, एक फ़ंक्शन अपने नाम के बजाय अपने पैरामीटर का उपयोग करके पुनरावृत्ति कर सकता है! तो दिए गए फ़ंक्शन Uमें कम से कम एक पैरामीटर होना चाहिए जो फ़ंक्शन (स्वयं) के लिए बाध्य होगा।

नीचे दिए गए उदाहरण में, हमारे पास कोई बाहर निकलने की स्थिति नहीं है, इसलिए हम बस अनिश्चित काल तक लूप करेंगे जब तक कि स्टैक ओवरफ्लो न हो जाए

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)

यू कॉम्बिनेटर का उपयोग करके किसी मौजूदा फ़ंक्शन से स्व-संदर्भ कैसे हटाया जाए

यहां मैं आपको दिखाऊंगा कि कैसे एक पुनरावर्ती फ़ंक्शन का उपयोग करें जो स्वयं के संदर्भ का उपयोग करता है और इसे एक फ़ंक्शन में बदल देता है जो कि आत्म संदर्भ के स्थान पर यू कॉम्बिनेटर को नियुक्त करता है

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

अब यू कॉम्बिनेटर का उपयोग करके आंतरिक संदर्भ को बदलने के लिए 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 कंबाइनेटर

सम्बंधित: यू और वाई कॉम्बिनेटरों ने एक दर्पण सादृश्य का उपयोग करके समझाया

पिछले अनुभाग में हमने देखा कि स्व-संदर्भ पुनरावृत्ति को एक पुनरावर्ती फ़ंक्शन में कैसे बदलना है जो यू कॉम्बिनेटर का उपयोग करके नामित फ़ंक्शन पर भरोसा नहीं करता है। वहाँ एक झुंझलाहट थोडा है के साथ याद करने के लिए हमेशा के लिए पहले तर्क के रूप में समारोह पास। खैर, वाई-कॉम्बिनेटर यू-कॉम्बिनेटर पर बनाता है और उस थकाऊ बिट को निकालता है। यह एक अच्छी बात है क्योंकि जटिलता को दूर करना / कम करना प्राथमिक कारण है जिससे हम कार्य करते हैं

सबसे पहले, हम अपने बहुत ही वाई-कॉम्बिनेटर को प्राप्त करते हैं

// 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 (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- आप देखेंगे कि प्रोग्राम लगभग समान हैं लेकिन वाई कॉम्बिनेटर चीजों को थोड़ा साफ रखता है

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


1 से अधिक पैरामीटर के साथ यू और वाई कॉम्बिनेटर

ऊपर के उदाहरणों में, हमने देखा कि कैसे हम अपनी गणना की "स्थिति" पर नज़र रखने के लिए एक तर्क और पास कर सकते हैं। लेकिन क्या होगा अगर हमें अतिरिक्त राज्य का ट्रैक रखने की आवश्यकता है?

हम एरियर या कुछ और जैसे कंपाउंड डेटा का उपयोग कर सकते हैं ...

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नीचे दी गई परिभाषा को देखें। हम तुरंत लागू कर रहे हैं 0और 1जो करने के लिए बाध्य कर रहे हैं aऔर bक्रमशः। अब केवल आपूर्ति के लिए अंतिम तर्क की प्रतीक्षा की जा रही है जो बाध्य होगा 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) और व्युत्पन्न 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 ]


यह सब असामान्य 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 पांचवें संस्करण के 'सख्त मोड' में अस्वीकृत है। हालांकि ECMA 3 और गैर-सख्त-मोड दूर नहीं जा रहे हैं, लेकिन सख्त मोड में काम करने से अधिक संभव भाषा अनुकूलन का वादा किया जाता है।

कोई इनलाइन फ़ंक्शन का नाम भी उपयोग कर सकता है:

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

हालाँकि, इनलाइन फ़ंक्शन एक्सप्रेशन को नामांकित भी सबसे अच्छा माना जाता है, क्योंकि IE का JScript उनके लिए कुछ बुरा काम करता है। उपरोक्त उदाहरण fooमें IE में माता-पिता के दायरे को गलत तरीके से प्रदूषित करता है, और माता-पिता fooको fooअंदर देखने के लिए एक अलग उदाहरण हैfoo

इसे इनलाइन अनाम फ़ंक्शन में डालने का क्या उद्देश्य है? यदि आप केवल मूल दायरे को प्रदूषित करने से बचना चाहते हैं, तो आप निश्चित रूप से अपना पहला उदाहरण किसी अन्य सेल्फ-कॉलिंग-अनाम-फ़ंक्शन (नेमस्पेस) के अंदर छिपा सकते हैं। क्या आपको वास्तव nothingमें पुनरावृत्ति के आसपास हर बार एक नई प्रति बनाने की आवश्यकता है ? आप एक ऐसे नाम स्थान के साथ बेहतर हो सकते हैं जिसमें दो सरल पारस्परिक रूप से पुनरावर्ती कार्य होते हैं।


मैं सहमत हूं, एक नामित फ़ंक्शन तर्कों की तुलना में अधिक उपयुक्त है। केवल एक्मास्क्रिप्ट सख्त मोड के लिए नहीं, बल्कि अनुकूलन के मामले के लिए भी। क्योंकि प्रत्येक पुनरावृत्ति पर उसे कैलली का संदर्भ प्राप्त करने की आवश्यकता होती है (और यह संभवतः निष्पादन की गति को कम कर सकता है। )

काव्य के लिए +1, "pushing against the boundaries of good taste"- (अच्छी तरह से, और अच्छी जानकारी)।
पीटर अजताई Peter

क्या एक सरल पूर्व / उपसर्ग के बारे में अगर प्रदूषण वास्तव में यहाँ एक चिंता का विषय है? यह देखते हुए कि यह वैश्विक दायरे में नहीं है (भले ही फ़ंक्शन शीर्ष lvl है, उसके पास पहले से ही एक अनाम फ़ंक्शन होना चाहिए जो उसके पूरे कोड को लपेटता है) यह वास्तव में संभावना नहीं है कि मूल नाम के recur_fooकार्य में एक नाम के साथ टकराव होगा (या बीमार होने के लिए) -उपयोग किया गया) ।
22

बहुत दिलचस्प - jsfiddle.net/hck2A - IE इस मामले में माता-पिता को प्रदूषित करता है, जैसा आपने कहा। कभी एहसास नहीं हुआ।
पीटर अज़ताई

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: यह सख्त मोड में और ES5 में अस्वीकृत है।
प्वाइंट्स

नीचे वोट दिया गया है, दलीलें। विवाद ईएस 5 में पदावनत है
जैम रोड्रिगेज

यह NodeJS में काम करता है। मैं ES5 के बारे में कम परवाह नहीं कर सकता था जब तक कि यह एक निश्चित वातावरण पर एक पूर्वानुमानित फैशन में काम करता है।
अंगद

1
यह एक टाइम बम है। "फिक्स्ड" वातावरण नामक कोई ऐसी चीज नहीं है, जैसा कि ऊपर दिए गए सुझाव में दिया गया है। ऐसा करने के हजारों कारणों में से किसी के कारण आप लगभग हमेशा अपग्रेड होते रहेंगे।
संपतश्री

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बयान के साथ पहले घोषित करने के साथ कर सकते हैं । डनो चाहे वह प्रश्न के नियमों को तोड़ता हो, लेकिन जैसा कि आपके पास अभी है, varबयान के बिना आपको ECMAScript / सख्त मोड में त्रुटि मिलेगी।
टिम डाउन

मेरी प्रारंभिक टिप्पणी में varकीवर्ड शामिल था , लेकिन एक बार जब मैंने इस कोड का परीक्षण किया, तो यह त्रुटियों को फेंक रहा था, क्योंकि आप वास्तव में एक स्व-आह्वान ब्लॉक के अंदर एक चर घोषित नहीं कर सकते हैं, और मेरा दृष्टिकोण एक अपरिभाषित चर की ऑटो घोषणा पर निर्भर करता है, और इसलिए "पॉइन्टीज़" समाधान अधिक सही है। लेकिन मैंने अभी भी Fabrizio Calderan जवाब के लिए मतदान किया;)
ArtBIT

हां, (var recur = function() {...})();यह काम नहीं करेगा क्योंकि यह अब असाइनमेंट एक्सप्रेशन के बजाय एक स्टेटमेंट है (जो असाइन किया गया मान लौटाता है)। मैं var recur; (recur = function() {...})();इसके बजाय सुझाव दे रहा था ।
टिम डाउन

3

जब आप इस तरह एक अनाम फ़ंक्शन घोषित करते हैं:

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

इसकी एक फ़ंक्शन अभिव्यक्ति मानी जाती है और इसका एक वैकल्पिक नाम होता है (जिसे आप इसे अपने भीतर से कॉल कर सकते हैं। लेकिन क्योंकि यह एक फ़ंक्शन एक्सप्रेशन है (और स्टेटमेंट नहीं है) यह गुमनाम रहता है (लेकिन इसका एक नाम है जिसे आप कॉल कर सकते हैं)। यह फ़ंक्शन स्वयं कॉल कर सकता है:

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

"यह गुमनाम रहता है" - नहीं यह नहीं है। एक अनाम फ़ंक्शन का कोई नाम नहीं है। मैं समझता हूं कि fooवर्तमान संदर्भ में घोषित नहीं किया गया है, लेकिन यह कम या ज्यादा अप्रासंगिक है। नाम के साथ एक फ़ंक्शन अभी भी एक नामित फ़ंक्शन है - अनाम नहीं
आप

3

फंक्शनलियो को ही फंक्शन पास क्यों नहीं करते?

    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

मुझे यकीन नहीं है कि यदि उत्तर अभी भी आवश्यक है, लेकिन फ़ंक्शन का उपयोग करके बनाए गए प्रतिनिधियों का उपयोग करके भी ऐसा किया जा सकता है:

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

इसमें नाम या कार्य शामिल नहीं हैं।


1

जैसे बोबिसन ने लिखा, बस अपने फंक्शन को नाम दें।

लेकिन, मैं अनुमान लगा रहा हूं कि आप भी एक प्रारंभिक मूल्य में पास होना चाहते हैं और अंततः अपने कार्य को रोक सकते हैं!

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.:। लेकिन ओपी को यह बात याद आती है ... :)
gblazex

@ गालम्ब - मैंने इसे पढ़ा। सख्त मोड में और ES5 में अस्वीकृत एक मूल क्षेत्र को प्रदूषित करने और अतिरिक्त उदाहरण बनाने के समान नहीं है।
पीटर अज़ताई

1

मुझे जरूरत थी (या बल्कि, चाहता था) एक एक-लाइनर अनाम फ़ंक्शन एक स्ट्रिंग के ऊपर किसी वस्तु के निर्माण के रास्ते पर चलने के लिए, और इस तरह से संभाला:

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

जो 'रूट: फू: बार: बज़: ...' जैसी स्ट्रिंग पैदा करता है।


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

एक अन्य उत्तर जिसमें नाम या फ़ंक्शन शामिल नहीं हैं

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

अच्छा: किसी स्थानीय पैरामीटर के लिए एक अनाम फ़ंक्शन को बांधें और फिर फ़ंक्शन को स्थानीय पैरामीटर के माध्यम से कॉल करें, लेकिन पुनरावृत्ति के लिए फ़ंक्शन को भी पास करें।
13

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

पहली पुनरावृत्ति को अनियंत्रित करने की कोई आवश्यकता नहीं थी। स्वयं को एक संदर्भ के रूप में प्राप्त करने वाला फ़ंक्शन ओओपी के प्रिमोर्डियल ओज में वापस आ जाता है।


0

यह तीर कार्यों के साथ @ zem के उत्तर का एक संस्करण है।

आप Uया Yकॉम्बिनेटर का उपयोग कर सकते हैं । वाई कॉम्बीनेटर उपयोग करने के लिए सबसे सरल है।

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

फिर भी एक और वाई-कॉम्बिनेटर समाधान, रोसेट्टा-कोड का उपयोग कर लिंक (मुझे लगता है कि किसी ने पहले स्टैकऑवरफ्लो पर कहीं लिंक का उल्लेख किया था।

तीर मेरे लिए अधिक पठनीय अनाम कार्यों के लिए हैं:

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);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.