अनाम पुनरावर्तन

From Vigyanwiki

कंप्यूटर विज्ञान में, एनोनिमस रिकर्सन रिकर्सन (कंप्यूटर विज्ञान) है जो स्पष्ट रूप से किसी फ़ंक्शन को नाम से नहीं बुलाता है। यह या तो स्पष्ट रूप से, उच्च-क्रम वाले फ़ंक्शन का उपयोग करके किया जा सकता है - किसी फ़ंक्शन को लॉजिक के रूप में सिद्ध करना और उसे कॉल करना या परोक्ष रूप से, रिफ्लेक्शन (कंप्यूटर प्रोग्रामिंग) सुविधाओं के माध्यम से जो किसी को वर्तमान संदर्भ, विशेष रूप से वर्तमान फ़ंक्शन या कभी-कभी वर्तमान फ़ंक्शन के कॉलिंग फ़ंक्शन के आधार पर कुछ कार्यों तक पहुंचने की अनुमति देता है।

प्रोग्रामिंग अभ्यास में, जावास्क्रिप्ट में एनोनिमस रिकर्सन का विशेष रूप से उपयोग किया जाता है, जो इसका समर्थन करने के लिए रिफ्लेक्शन सुविधाएं प्रदान करता है। चूँकि, सामान्य प्रोग्रामिंग अभ्यास में, इसे व्यर्थ शैली माना जाता है, और इसके अतिरिक्त नामांकित कार्यों के साथ रिकर्सन का सुझाव दिया जाता है। इस प्रकार कार्यों को लॉजिक के रूप में स्पष्ट रूप से सिद्ध करने के माध्यम से एनोनिमस रिकर्सन किसी भी लैंग्वेज में संभव है जो लॉजिक के रूप में कार्यों का समर्थन करती है, चूँकि व्यवहार में इसका उपयोग संभवतः ही कभी किया जाता है, क्योंकि यह नाम से स्पष्ट रूप से रिकर्सन की तुलना में अधिक लंबा और कम स्पष्ट है।

सैद्धांतिक कंप्यूटर विज्ञान में, एनोनिमस रिकर्सन महत्वपूर्ण है, क्योंकि यह दर्शाता है कि कोई नामांकित कार्यों की आवश्यकता के बिना रिकर्सन प्रयुक्त कर सकता है। यह लैम्ब्डा कैलकुलस के लिए विशेष रूप से महत्वपूर्ण है, जिसमें एनोनिमस यूनरी फ़ंक्शन हैं, किन्तु किसी भी पुनरावर्ती फ़ंक्शन की गणना करने में सक्षम है। इस एनोनिमस रिकर्सन को निश्चित-बिंदु कॉम्बिनेटर के माध्यम से सामान्य रूप से उत्पादित किया जा सकता है।

उपयोग

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

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

एनोनिमस रिकर्सन का उपयोग नामांकित फ़ंक्शंस के लिए भी किया जा सकता है, किन्तु उन्हें नाम से कॉल करना होता है, यह निर्दिष्ट करना कि कोई वर्तमान फ़ंक्शन पर रिकर्सन कर रहा है, या किसी को उस नाम को परिवर्तन करने की आवश्यकता के बिना फ़ंक्शन का नाम परिवर्तन करने की अनुमति देना जहां यह स्वयं कॉल करता है। चूँकि, प्रोग्रामिंग शैली के स्थिति में यह सामान्यतः नहीं किया जाता है।

विकल्प

नामांकित फ़ंक्शन

सामान्य विकल्प नामांकित फ़ंक्शंस और नामांकित रिकर्सन का उपयोग करना है। एनोनिमस फ़ंक्शन को देखते हुए, यह या तो फ़ंक्शन में नाम बाइंड करके किया जा सकता है, जैसा कि जावास्क्रिप्ट में नामांकित फ़ंक्शन एक्सप्रेशन में होता है, या फ़ंक्शन को वेरिएबल में निर्दिष्ट करके और फिर वेरिएबल को कॉल करके किया जा सकता है, जैसा कि जावास्क्रिप्ट में फ़ंक्शन स्टेटमेंट में होता है। चूँकि जो लैंग्वेजएँ एनोनिमस फ़ंक्शंस की अनुमति देती हैं, वह सामान्यतः इन फ़ंक्शंस को वेरिएबल्स (यदि प्रथम श्रेणी फ़ंक्शंस नहीं) को निर्दिष्ट करने की अनुमति देती हैं, तो अनेक लैंग्वेजएँ स्वयं फ़ंक्शन को संदर्भित करने का कोई विधि प्रदान नहीं करती हैं, और स्पष्ट रूप से एनोनिमस रिकर्सन को अस्वीकार करती हैं; उदाहरणों में गो (प्रोग्रामिंग लैंग्वेज) सम्मिलित है।[1]

उदाहरण के लिए, जावास्क्रिप्ट में फैक्टोरियल फ़ंक्शन को एनोनिमस रिकर्सन के माध्यम से परिभाषित किया जा सकता है:[2]

[1, 2, 3, 4, 5].map(function(n) {
     return (!(n > 1)) ? 1 : arguments.callee(n-1) * n;
});

नामांकित फ़ंक्शन अभिव्यक्ति का उपयोग करने के लिए पुनः लिखे जाने पर यह प्राप्त होता है:

[1, 2, 3, 4, 5].map(function factorial(n) {
     return (!(n > 1)) ? 1 : factorial(n-1) * n;
});

फ़ंक्शंस को लॉजिक के रूप में पास करना

वर्तमान फ़ंक्शन या कॉलिंग फ़ंक्शन को संदर्भित करने के लिए तंत्र के बिना भी, ऐसी लैंग्वेज में एनोनिमस रिकर्सन संभव है जो फ़ंक्शन को लॉजिक के रूप में अनुमति देता है। यह मूल पुनरावर्ती फ़ंक्शन में और मापदंड जोड़कर और पुनरावर्ती कॉल के लिए फ़ंक्शन के रूप में इस मापदंड का उपयोग करके किया जाता है। यह उच्च-क्रम फ़ंक्शन बनाता है, और इस उच्च फ़ंक्शन को पास करने से वास्तविक पुनरावर्ती फ़ंक्शन के अन्दर एनोनिमस रिकर्सन की अनुमति मिलती है। इस उच्च क्रम फ़ंक्शन पर निश्चित-बिंदु कॉम्बिनेटर प्रयुक्त करके इसे पूरी तरह से गुमनाम रूप से किया जा सकता है। यह मुख्य रूप से अकादमिक रुचि का है, विशेष रूप से यह दिखाने के लिए कि लैम्ब्डा कैलकुलस में रिकर्सन है, क्योंकि परिणामी अभिव्यक्ति मूल नामांकित रिकर्सिव फ़ंक्शन की तुलना में अधिक सम्मिश्र है। इसके विपरीत, फिक्स्ड-पॉइंटेड कॉम्बिनेटर्स के उपयोग को सामान्य रूप से एनोनिमस रिकर्सन के रूप में संदर्भित किया जा सकता है, क्योंकि यह उनका उल्लेखनीय उपयोग है, चूँकि उनके पास अन्य अनुप्रयोग हैं।[3][4]

इसे नीचे पायथन फर्स्ट रिकर्सन नामक मानक का उपयोग करके चित्रित किया गया है:

def fact(n):
    if n == 0:
        return 1
    return n * fact(n - 1)

उच्च-क्रम फ़ंक्शन का उपयोग करना जिससे टॉप-लेबल फ़ंक्शन किसी लॉजिक पर अनोनीमौसली रूप से रिकर्सन कर सकते है, किन्तु फिर भी लॉजिक के रूप में मानक पुनरावर्ती फ़ंक्शन की आवश्यकता होती है:

def fact0(n0):
    if n0 == 0:
        return 1
    return n0 * fact0(n0 - 1)
fact1 = lambda f, n1: 1 if n1 == 0 else n1 * f(n1 - 1)
fact = lambda n: fact1(fact0, n)

हम फ़ंक्शन लॉजिक को कॉल में पास करके मानक पुनरावर्ती फ़ंक्शन को समाप्त कर सकते हैं:

fact1 = lambda f, n1: 1 if n1 == 0 else n1 * f(f, n1 - 1)
fact = lambda n: fact1(fact1, n)

दूसरी पंक्ति को सामान्य हायर-आर्डर फ़ंक्शन द्वारा प्रतिस्थापित किया जा सकता है जिसे कॉम्बिनेटर लॉजिक कहा जाता है:

F = lambda f: (lambda x: f(f, x))
fact1 = lambda f, n1: 1 if n1 == 0 else n1 * f(f, n1 - 1)
fact = F(fact1)

एनोनिमस रूप से लिखा गया:[5]

(lambda f: (lambda x: f(f, x))) \
(lambda g, n1: 1 if n1 == 0 else n1 * g(g, n1 - 1))

लैम्ब्डा कैलकुलस में, जो केवल ही चर के कार्यों का उपयोग करता है, यह फिक्स्ड-पॉइंट कॉम्बिनेटर के माध्यम से किया जा सकता है। पहले दो वेरिएबल्स के उच्च-क्रम वाले फ़ंक्शन को एकल वेरिएबल का फ़ंक्शन बनाएं, जो कर्र्यिंग द्वारा सीधे फ़ंक्शन लौटाता है:

fact1 = lambda f: (lambda n1: 1 if n1 == 0 else n1 * f(f)(n1 - 1))
fact = fact1(fact1)

यहां दो हायर-आर्डर फ़ंक्शन को अपने आप में प्रयुक्त करने वाले ऑपरेशन हैं: f(f) पहली पंक्ति में और fact1(fact1) क्षण में। कॉम्बिनेटरी लॉजिक में दूसरे दोहरे अनुप्रयोग का गुणनखंड करने से परिणाम मिलते हैं:

C = lambda x: x(x)
fact1 = lambda f: (lambda n1: 1 if n1 == 0 else n1 * f(f)(n1 - 1))
fact = C(fact1)

अन्य दोहरे अनुप्रयोग प्रतिफलों को ध्यान में रखते हुए:

C = lambda x: x(x)
D = lambda f: (lambda x: f(lambda v: x(x)(v)))
fact1 = lambda g: (lambda n1: 1 if n1 == 0 else n1 * g(n1 - 1))
fact = C(D(fact1))

दो कॉम्बिनेटरों को में मिलाने से फिक्स्ड-पॉइंट कॉम्बिनेटर प्राप्त होता है:

C = lambda x: x(x)
D = lambda f: (lambda x: f(lambda v: x(x)(v)))
Y = lambda y: C(D(y))
fact1 = lambda g: (lambda n1: 1 if n1 == 0 else n1 * g(n1 - 1))
fact = Y(fact1)

Y कॉम्बिनेटर का विस्तार करने से परिणाम मिलते हैं:

Y = lambda f: (lambda x: f(lambda v: x(x)(v))) \
              (lambda x: f(lambda v: x(x)(v)))
fact1 = lambda g: (lambda n1: 1 if n1 == 0 else n1 * g(n1 - 1))
fact = Y(fact1)

इनके संयोजन से लैम्ब्डा कैलकुलस (एकल चर के एनोनिमस कार्य) में फैक्टोरियल की पुनरावर्ती परिलैंग्वेज प्राप्त होती है:[6]

(lambda f: (lambda x: f(lambda v: x(x)(v)))
           (lambda x: f(lambda v: x(x)(v)))) \
(lambda g: (lambda n1: 1 if n1 == 0 else n1 * g(n1 - 1)))

उदाहरण

एपीएल

एपीएल (प्रोग्रामिंग लैंग्वेज) में वर्तमान डीएफएनएस के माध्यम से पहुंच योग्य है। यह एनोनिमस रिकर्सन की अनुमति देता है, जैसे कि फैक्टोरियल के इस कार्यान्वयन में:

   {0=⍵:1  × -1} 5
120
   {0=⍵:1  × -1}¨ 10    ⍝ applied to each element of 0 to 9
1 1 2 6 24 120 720 5040 40320 362880

जावास्क्रिप्ट

जावास्क्रिप्ट में, वर्तमान फ़ंक्शन arguments.callee के माध्यम से एक्सेस किया जा सकता है , जबकि कॉलिंग फ़ंक्शन को arguments.caller माध्यम से पहुंच योग्य है ये एनोनिमस रिकर्सन की अनुमति देते हैं, जैसे कि फैक्टोरियल के इस कार्यान्वयन में:[2]

[1, 2, 3, 4, 5].map(function(n) {
    return (!(n > 1)) ? 1 : arguments.callee(n - 1) * n;
});

पर्ल

पर्ल 5.16 से प्रारंभ होकर, वर्तमान सबरूटीन __SUB__ माध्यम से पहुंच योग्य है जो वर्तमान सबरूटीन का संदर्भ लौटाता है, या इस प्रकारundef सबरूटीन के बाहर.[7] यह एनोनिमस रिकर्सन की अनुमति देता है, जैसे कि फैक्टोरियल के निम्नलिखित कार्यान्वयन में:

#!/usr/bin/perl
use feature ":5.16";

print sub {
    my $x = shift;
    $x > 0
    ? $x * __SUB__->( $x - 1 )
    : 1;
}->(5), "\n";

R

R (प्रोग्रामिंग लैंग्वेज) में, वर्तमान फ़ंक्शन का उदाहरण के लिए Recall उपयोग करके कॉल किया जा सकता है

sapply(0:5, function(n) {
  if (n == 0) return(1)
  n * Recall(n - 1)
})

चूँकि, यह काम नहीं करेगा, यदि इसे किसी अन्य फ़ंक्शन के लिए लॉजिक के रूप में सिद्ध किया जाता है, उदाहरण के लिए एनोनिमस फ़ंक्शन परिलैंग्वेज के अंदर lapply, है। इस स्थिति में, sys.function(0) का उपयोग किया जा सकता है।[8] उदाहरण के लिए, नीचे दिया गया कोड किसी सूची को पुनरावर्ती रूप से वर्गित करता है:

(function(x) {
  if (is.list(x)) {
    lapply(x, sys.function(0))
  } else {
    x^2
  }
})(list(list(1, 2, 3), list(4, 5)))

संदर्भ

  1. Issue 226: It's impossible to recurse a anonymous function in Go without workarounds.
  2. 2.0 2.1 answer by olliej, Oct 25 '08 to "Why was the arguments.callee.caller property deprecated in JavaScript?", StackOverflow
  3. This terminology appear to be largely folklore, but it does appear in the following:
    • Trey Nash, Accelerated C# 2008, Apress, 2007, ISBN 1-59059-873-3, p. 462—463. Derived substantially from Wes Dyer's blog (see next item).
    • Wes Dyer Anonymous Recursion in C#, February 02, 2007, contains a substantially similar example found in the book above, but accompanied by more discussion.
  4. The If Works Deriving the Y combinator, January 10th, 2008
  5. Hugo Walter's answer to "Can a lambda function call itself recursively in Python?"
  6. Nux's answer to "Can a lambda function call itself recursively in Python?"
  7. Perldoc, "The 'current_sub' feature", perldoc feature
  8. agstudy's answer to Get currently called function to write anonymous recursive function at StackOverflow