वाई-कॉम्बिनेटर क्या है? [बन्द है]


392

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

  • वाई-कॉम्बिनेटर क्या है?
  • कॉम्बिनेटर कैसे काम करते हैं?
  • वे किस चीज़ लिए अच्छे हैं?
  • क्या वे प्रक्रियात्मक भाषाओं में उपयोगी हैं?

12
एक टिप का थोड़ा सा, अगर आप कार्यात्मक भाषाओं के बारे में सीख रहे हैं जैसे मैं करता हूं, तो बेहतर है कि जब तक आप इसके साथ सहज नहीं हो जाते हैं, तब तक कॉम्बिनेटर छोड़ दें, अन्यथा यह पागलपन का रास्ता है ...
इगोर ज़ेवाका



1
मैंने Y कम्बिनेटर की अपनी समझ को साझा करते हुए एक छोटी सी बात लिखी: gist.github.com/houtianze/b274e4b975a28fe08aee681699c3f7d0 मैंने समझाया (मेरी समझ से) कि कैसे "Y Combinator पुनरावर्ती कार्य करता है"
ibic

1
यह प्रश्न "बहुत व्यापक" कैसे है?
री मियासका

जवाबों:


201

यदि आप लंबे समय से पढ़ने के लिए तैयार हैं, तो माइक वनियर के पास एक बढ़िया स्पष्टीकरण है । छोटी कहानी, यह आपको एक ऐसी भाषा में पुनरावृत्ति को लागू करने की अनुमति देती है जो मूल रूप से इसका समर्थन नहीं करती है।


14
हालांकि यह एक लिंक से थोड़ा अधिक है; यह एक बहुत ही संक्षिप्त सारांश के साथ एक कड़ी है । एक लंबे सारांश की सराहना की जाएगी।
मार्टिन पीटर्स

2
यह सिर्फ एक लिंक है लेकिन यह इससे बेहतर नहीं हो सकता। यह जवाब बाहर निकलने के लिए कोई आधार मामले की स्थिति के साथ (Add1 वोट) का हकदार है; उर्फ अनंत पुनरावर्ती।
यवर

7
@Andre MacFie: मैंने प्रयास पर टिप्पणी नहीं की, मैंने गुणवत्ता पर टिप्पणी की। सामान्य तौर पर, स्टैक ओवरफ्लो पर नीति यह है कि उत्तर अधिक जानकारी के लिंक के साथ स्वयं निहित होना चाहिए।
जोर्जेन फॉग

1
@galdre सही है। यह एक बेहतरीन लिंक है, लेकिन यह सिर्फ एक लिंक है। नीचे 3 अन्य उत्तरों में भी इसका उल्लेख किया गया है, लेकिन केवल एक सहायक दस्तावेज के रूप में, क्योंकि वे अपने आप सभी अच्छे स्पष्टीकरण देते हैं। यह जवाब भी ओपी के सवालों का जवाब देने का प्रयास नहीं करता है।
टॉरिट्टे

290

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

यह उन भाषाओं में लागू होता है जो लैम्ब्डा कार्यों का समर्थन करती हैंअभिव्यक्ति lambdas की आधारित प्रकृति आम तौर पर मतलब है कि वे नाम से खुद को करने का उल्लेख नहीं कर सकते हैं। और चर की घोषणा के माध्यम से इसके चारों ओर काम करना, इसे संदर्भित करना, फिर लैम्बडा को इसे सौंपना, स्व-संदर्भ लूप को पूरा करने के लिए, भंगुर है। लैम्ब्डा वेरिएबल को कॉपी किया जा सकता है, और मूल वेरिएबल को फिर से असाइन किया जाता है, जो सेल्फ-रेफरेंस को तोड़ता है।

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

नीचे एक उदाहरण है कि C # में Y-Combinator का उपयोग और कार्य कैसे किया जाता है।

वाई-कॉम्बिनेटर के उपयोग से पुनरावर्ती कार्य के निर्माण का एक "असामान्य" तरीका शामिल है। पहले आपको अपना फ़ंक्शन कोड के एक टुकड़े के रूप में लिखना होगा जो पहले से मौजूद फ़ंक्शन को कॉल करता है, न कि स्वयं:

// Factorial, if func does the same thing as this bit of code...
x == 0 ? 1: x * func(x - 1);

फिर आप उस फ़ंक्शन को कॉल करने के लिए फ़ंक्शन को चालू करते हैं, और ऐसा करने वाला फ़ंक्शन लौटाते हैं। इसे कार्यात्मक कहा जाता है, क्योंकि यह एक फ़ंक्शन लेता है, और इसके साथ एक ऑपरेशन करता है जिसके परिणामस्वरूप एक और फ़ंक्शन होता है।

// A function that creates a factorial, but only if you pass in
// a function that does what the inner function is doing.
Func<Func<Double, Double>, Func<Double, Double>> fact =
  (recurs) =>
    (x) =>
      x == 0 ? 1 : x * recurs(x - 1);

अब आपके पास एक फ़ंक्शन है जो एक फ़ंक्शन लेता है, और एक अन्य फ़ंक्शन लौटाता है, जो एक फैक्टरियल की तरह दिखता है, लेकिन खुद को कॉल करने के बजाय, यह बाहरी फ़ंक्शन में दिए गए तर्क को कॉल करता है। आप इस तथ्य को कैसे बनाते हैं? स्वयं के लिए आंतरिक फ़ंक्शन पास करें। वाई-कॉम्बिनेटर एक स्थायी नाम के साथ एक फ़ंक्शन होने के द्वारा करता है, जो पुनरावृत्ति का परिचय दे सकता है।

// One-argument Y-Combinator.
public static Func<T, TResult> Y<T, TResult>(Func<Func<T, TResult>, Func<T, TResult>> F)
{
  return
    t =>  // A function that...
      F(  // Calls the factorial creator, passing in...
        Y(F)  // The result of this same Y-combinator function call...
              // (Here is where the recursion is introduced.)
        )
      (t); // And passes the argument into the work function.
}

अपने आप को कॉल करने वाले तथ्य के बजाय, क्या होता है कि फैक्टरियल, फैक्टरियल जनरेटर को कॉल करता है (वाई-कॉम्बिनेटर को पुनरावर्ती कॉल द्वारा लौटाया जाता है)। और टी के वर्तमान मूल्य के आधार पर जनरेटर से लौटाया गया फ़ंक्शन या तो जनरेटर को फिर से कॉल करेगा, टी - 1 के साथ, या सिर्फ 1 वापस करेगा, पुनरावृत्ति को समाप्त कर देगा।

यह जटिल और गूढ़ है, लेकिन यह सब रन-टाइम पर निकलता है, और इसके कार्य की कुंजी "आस्थगित निष्पादन" है, और दो कार्यों को पूरा करने के लिए पुनरावृत्ति का टूटना। आंतरिक एफ को एक तर्क के रूप में पारित किया जाता है , जिसे अगले पुनरावृत्ति में कहा जाता है, केवल यदि आवश्यक हो


5
ओह, आपको इसे 'वाई' और पैरामीटर 'एफ' क्यों कहा जाना था! वे बस प्रकार के तर्कों में खो जाते हैं!
ब्रायन हेंक

3
हास्केल में, आप के साथ पुनरावृत्ति को रोक सकते हैं: fix :: (a -> a) -> aऔर aबदले में आप जितने चाहें उतने तर्क का कार्य कर सकते हैं। इसका मतलब है कि स्थैतिक टाइपिंग वास्तव में इस बोझिल नहीं बनाता है।
पीकर

12
माइक वेनियर के विवरण के अनुसार, वाई के लिए आपकी परिभाषा वास्तव में एक कॉम्बिनेटर नहीं है क्योंकि यह पुनरावर्ती है। "(सबसे) स्पष्ट पुनरावर्तन (आलसी संस्करण)" के तहत वह आपके सी # कोड के बराबर आलसी योजना है, लेकिन अंक 2 में बताता है: "यह एक संयोजन नहीं है, क्योंकि परिभाषा के शरीर में वाई एक मुक्त चर है जो केवल एक बार जब परिभाषा पूरी हो जाती है ... "मुझे लगता है कि वाई-कॉम्बिनेटरों के बारे में अच्छी बात यह है कि वे किसी फ़ंक्शन के निश्चित-बिंदु का मूल्यांकन करके पुनरावृत्ति उत्पन्न करते हैं। इस तरह, उन्हें स्पष्ट पुनरावृत्ति की आवश्यकता नहीं है।
ग्रांटज जूल

@ क्रांति आप एक अच्छा बिंदु बनाते हैं। मुझे इस उत्तर को पोस्ट करते हुए कुछ साल हो गए हैं। अब मैं देख रहा हूँ कि मैंने वाई लिखा है, लेकिन वाई-कॉम्बिनेटर नहीं। मैं जल्द ही फिर से उनकी पोस्ट पढ़ूंगा और देखूंगा कि क्या मैं एक सुधार पोस्ट कर सकता हूं। मेरी आंत मुझे चेतावनी दे रही है कि सी # की सख्त स्थैतिक टाइपिंग इसे अंत में रोक सकती है, लेकिन मैं देखूंगा कि मैं क्या कर सकता हूं।
क्रिस अम्मारमैन

1
@WayneBurkett यह गणित में एक बहुत ही आम बात है।
योतेंगो यूएनएलसीडी

102

मैंने इसे http://www.mail-archive.com/boston-pm@mail.pm.org/msg02716.html से हटा लिया है जो एक स्पष्टीकरण है जो मैंने कई साल पहले लिखा था।

मैं इस उदाहरण में जावास्क्रिप्ट का उपयोग करूँगा, लेकिन कई अन्य भाषाएँ भी काम करेंगी।

हमारा लक्ष्य केवल 1 चर और कोई असाइनमेंट के कार्यों का उपयोग करके 1 चर का एक पुनरावर्ती कार्य लिखने में सक्षम होना है, आदि नाम से चीजों को परिभाषित करना, (यह हमारा लक्ष्य क्यों है यह एक और सवाल है, चलो इसे चुनौती के रूप में लें फिर दिया।) असंभव लगता है, हुह? एक उदाहरण के रूप में, चलो भाज्य लागू करते हैं।

अच्छी तरह से चरण 1 यह कहना है कि यदि हम थोड़ा धोखा दिया है तो हम आसानी से ऐसा कर सकते हैं। 2 चर और असाइनमेंट के कार्यों का उपयोग करके हम कम से कम पुनरावृत्ति की स्थापना के लिए असाइनमेंट का उपयोग करने से बच सकते हैं।

// Here's the function that we want to recurse.
X = function (recurse, n) {
  if (0 == n)
    return 1;
  else
    return n * recurse(recurse, n - 1);
};

// This will get X to recurse.
Y = function (builder, n) {
  return builder(builder, n);
};

// Here it is in action.
Y(
  X,
  5
);

अब देखते हैं कि क्या हम कम धोखा खा सकते हैं। अच्छी तरह से सबसे पहले हम असाइनमेंट का उपयोग कर रहे हैं, लेकिन हमें इसकी आवश्यकता नहीं है। हम सिर्फ X और Y इनलाइन लिख सकते हैं।

// No assignment this time.
function (builder, n) {
  return builder(builder, n);
}(
  function (recurse, n) {
    if (0 == n)
      return 1;
    else
      return n * recurse(recurse, n - 1);
  },
  5
);

लेकिन हम 1 चर का एक फ़ंक्शन प्राप्त करने के लिए 2 चर के कार्यों का उपयोग कर रहे हैं। क्या हम इसे ठीक कर सकते हैं? वैसे हास्केल करी के नाम से एक स्मार्ट आदमी के पास एक साफ-सुथरी चाल है, अगर आपके पास अच्छे उच्च कार्य हैं तो आपको केवल 1 चर के कार्यों की आवश्यकता है। सबूत यह है कि आप 2 (या सामान्य स्थिति में अधिक) के कार्यों से प्राप्त कर सकते हैं 1 से चर के लिए एक विशुद्ध रूप से यांत्रिक पाठ परिवर्तन के साथ इस प्रकार है:

// Original
F = function (i, j) {
  ...
};
F(i,j);

// Transformed
F = function (i) { return function (j) {
  ...
}};
F(i)(j);

जहां ... बिल्कुल वैसा ही रहता है। (इस ट्रिक को इसके आविष्कारक के बाद "करीइंग" कहा जाता है। हास्केल को हास्केल करी के लिए भी भाषा का नाम दिया गया है। बेकार ट्रिविया के तहत फाइल करें।) अब बस इस परिवर्तन को हर जगह लागू करें और हम अपना अंतिम संस्करण प्राप्त करें।

// The dreaded Y-combinator in action!
function (builder) { return function (n) {
  return builder(builder)(n);
}}(
  function (recurse) { return function (n) {
    if (0 == n)
      return 1;
    else
      return n * recurse(recurse)(n - 1);
  }})(
  5
);

बेझिझक इसे आज़माएं। चेतावनी () जो वापस आती है, उसे एक बटन पर बाँध दें, जो भी हो। यह कोड 2 वैरिएबल्स के असाइनमेंट, डिक्लेरेशन या फ़ंक्शंस का उपयोग किए बिना, पुनर्मूल्यांकन, पुनरावृत्ति की गणना करता है। (लेकिन यह पता लगाने की कोशिश करना कि यह कैसे काम करता है आपके सिर को स्पिन करने की संभावना है। और इसे सौंपने के बिना, व्युत्पत्ति के बिना, बस थोड़ा सुधारित कोड में परिणाम होगा जो चकरा देना और भ्रमित होना निश्चित है।)

आप उन 4 पंक्तियों को प्रतिस्थापित कर सकते हैं जो पुनरावर्ती रूप से किसी अन्य पुनरावर्ती फ़ंक्शन के साथ फैक्टरियल को परिभाषित करते हैं जो आप चाहते हैं।


अच्छी व्याख्या। आपने function (n) { return builder(builder)(n);}इसके बजाय क्यों लिखा builder(builder)?
v7d8dpo4

@ v7d8dpo4 क्योंकि मैं 2 वेरिएबल्स के फंक्शन को एक वेरिएबल के फंक्शन में कर रहा हूं जो कि एक वेरिएशन का उपयोग कर रहा है।
btilly

क्या यही कारण है कि हमें बंद करने की आवश्यकता है?
चेतन

1
@ चेतन क्लोजर हमें एक अनाम फ़ंक्शन के लिए कॉल के पीछे अनुकूलित व्यवहार को टाई करने देता है। यह सिर्फ एक और अमूर्त तकनीक है।
btilly

85

मुझे आश्चर्य है कि जमीन से इसे बनाने के प्रयास में कोई फायदा नहीं है। चलो देखते हैं। यहाँ एक बुनियादी, पुनरावर्ती तथ्यात्मक कार्य है:

function factorial(n) {
    return n == 0 ? 1 : n * factorial(n - 1);
}

आइए रिफ्लेक्टर बनाते हैं और एक नया फ़ंक्शन बनाते हैं, जो factस्वयं गणना करने के बजाय एक अनाम फैक्टरियल-कंप्यूटिंग फ़ंक्शन देता है:

function fact() {
    return function(n) {
        return n == 0 ? 1 : n * fact()(n - 1);
    };
}

var factorial = fact();

यह थोड़ा अजीब है, लेकिन इसमें कुछ भी गलत नहीं है। हम प्रत्येक चरण में एक नया फैक्टरियल फ़ंक्शन उत्पन्न कर रहे हैं।

इस स्तर पर पुनरावृत्ति अभी भी काफी स्पष्ट है। factसमारोह अपने स्वयं के नाम के बारे में पता होना चाहिए। आइए पुनरावर्ती कॉल को मापें:

function fact(recurse) {
    return function(n) {
        return n == 0 ? 1 : n * recurse(n - 1);
    };
}

function recurser(x) {
    return fact(recurser)(x);
}

var factorial = fact(recurser);

यह बहुत अच्छा है, लेकिन recurserअभी भी इसका अपना नाम जानना आवश्यक है। चलो इसे भी मानकीकृत करें:

function recurser(f) {
    return fact(function(x) {
        return f(f)(x);
    });
}

var factorial = recurser(recurser);

अब, recurser(recurser)सीधे कॉल करने के बजाय , आइए एक रैपर फ़ंक्शन बनाएं, जो अपना परिणाम देता है:

function Y() {
    return (function(f) {
        return f(f);
    })(recurser);
}

var factorial = Y();

अब हम recurserपूरी तरह से नाम से छुटकारा पा सकते हैं ; यह वाई के आंतरिक फ़ंक्शन का एक तर्क है, जिसे फ़ंक्शन के साथ ही बदला जा सकता है:

function Y() {
    return (function(f) {
        return f(f);
    })(function(f) {
        return fact(function(x) {
            return f(f)(x);
        });
    });
}

var factorial = Y();

अभी भी संदर्भित केवल बाहरी नाम है fact, लेकिन यह अब तक स्पष्ट हो जाना चाहिए कि यह आसानी से मानकीकृत है, भी, पूर्ण, सामान्य, समाधान बना रहा है:

function Y(le) {
    return (function(f) {
        return f(f);
    })(function(f) {
        return le(function(x) {
            return f(f)(x);
        });
    });
}

var factorial = Y(function(recurse) {
    return function(n) {
        return n == 0 ? 1 : n * recurse(n - 1);
    };
});

जावास्क्रिप्ट में इसी तरह की व्याख्या: igstan.ro/posts/…
Pops

1
जब आपने फ़ंक्शन शुरू किया तो आपने मुझे खो दिया recurser। मामूली विचार नहीं कि यह क्या कर रहा है, या क्यों।
मूर्रे

2
हम उन कार्यों के लिए एक सामान्य पुनरावर्ती समाधान बनाने की कोशिश कर रहे हैं जो स्पष्ट रूप से पुनरावर्ती नहीं हैं। recurserसमारोह, इस लक्ष्य की ओर पहला कदम है, क्योंकि यह हम में से एक पुनरावर्ती संस्करण देता है factउस नाम से ही संदर्भ देता है कभी नहीं।
वेन

@WayneBurkett, Can मैं इस तरह Y Combinator पुनर्लेखन: function Y(recurse) { return recurse(recurse); } let factorial = Y(creator => value => { return value == 0 ? 1 : value * creator(creator)(value - 1); });। और यह है कि मैं इसे कैसे पचाता हूं (यदि यह सही है तो निश्चित नहीं है): फ़ंक्शन को स्पष्ट रूप से संदर्भित नहीं करके ( कॉम्बिनेटर के रूप में अनुमति नहीं है ), हम दो आंशिक रूप से लागू / करी कार्यों (एक निर्माता फ़ंक्शन और गणना फ़ंक्शन) का उपयोग कर सकते हैं , हम लैंबडा / अनाम फ़ंक्शन बना सकते हैं जो गणना फ़ंक्शन के लिए एक नाम की आवश्यकता के बिना पुनरावर्ती प्राप्त करते हैं?
neevek

50

ऊपर दिए गए अधिकांश उत्तर बताते हैं कि वाई-कॉम्बिनेटर क्या है, लेकिन इसके लिए क्या नहीं है ।

फिक्स्ड बिंदु combinators पता चलता है कि उपयोग किया जाता है लैम्ब्डा पथरी है पूरा ट्यूरिंग । यह गणना के सिद्धांत में एक बहुत ही महत्वपूर्ण परिणाम है और कार्यात्मक प्रोग्रामिंग के लिए एक सैद्धांतिक आधार प्रदान करता है

फिक्स्ड पॉइंट कॉम्बिनेटरों का अध्ययन करने से मुझे वास्तव में कार्यात्मक प्रोग्रामिंग को समझने में मदद मिली है। हालांकि मुझे वास्तविक प्रोग्रामिंग में उनके लिए कोई उपयोग नहीं मिला।


24

y-combinator in JavaScript :

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

var factorial = Y(function(recurse) {
  return function(x) {
    return x == 0 ? 1 : x * recurse(x-1);
  };
});

factorial(5)  // -> 120

संपादित करें : मैं कोड को देखने से बहुत कुछ सीखता हूं, लेकिन यह कुछ पृष्ठभूमि के बिना निगलने के लिए थोड़ा कठिन है - इसके बारे में क्षमा करें। अन्य उत्तरों द्वारा प्रस्तुत कुछ सामान्य ज्ञान के साथ, आप जो हो रहा है उसे अलग करना शुरू कर सकते हैं।

Y फ़ंक्शन "y-combinator" है। अब उस var factorialरेखा पर एक नज़र डालें जहां वाई का उपयोग किया जाता है। ध्यान दें कि आप एक फ़ंक्शन पास करते हैं जिसमें एक पैरामीटर है (इस उदाहरण में recurse) , जिसका उपयोग बाद में आंतरिक फ़ंक्शन में भी किया जाता है। पैरामीटर नाम मूल रूप से आंतरिक फ़ंक्शन का नाम बन जाता है जो इसे पुनरावर्ती कॉल करने की अनुमति देता है (क्योंकि यह recurse()परिभाषा में उपयोग करता है।) वाई-कॉम्बिनेटर फ़ंक्शन के पैरामीटर नाम के साथ अन्यथा अनाम आंतरिक फ़ंक्शन को जोड़ने का जादू करता है। वाई

Y कैसे जादू करता है, इसकी पूरी व्याख्या के लिए, लिंक किए गए लेख को देखें (मेरे द्वारा btw नहीं)।


6
अनाम पुनरावृत्ति करने के लिए जावास्क्रिप्ट को Y- कॉम्बीनेटर की आवश्यकता नहीं है क्योंकि आप तर्कों के साथ वर्तमान फ़ंक्शन तक पहुंच सकते हैं। देखें। en.wikipedia.org/wiki/… )
xitrium

6
arguments.calleeस्ट्रिक्ट मोड में अनुमति नहीं है: developer.mozilla.org/en/JavaScript/…
dave1010

2
आप अभी भी किसी भी फ़ंक्शन को एक नाम दे सकते हैं, और यदि यह फ़ंक्शन अभिव्यक्ति है, तो वह नाम केवल फ़ंक्शन के अंदर ही जाना जाता है। (function fact(n){ return n <= 1? 1 : n * fact(n-1); })(5)
एस्किला


18

प्रोग्रामर के लिए, जिन्हें गहराई से कार्यात्मक प्रोग्रामिंग का सामना नहीं करना पड़ा है, और अब शुरू करने की परवाह नहीं है, लेकिन हल्के ढंग से उत्सुक हैं:

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

यह फ़ंक्शन को तर्क के रूप में स्वयं पास करके काम करता है, इसलिए यह स्वयं को कॉल कर सकता है।

यह लैम्ब्डा कैलकुलस का हिस्सा है, जो वास्तव में मैथ्स है, लेकिन प्रभावी रूप से एक प्रोग्रामिंग लैंग्वेज है, और यह कंप्यूटर साइंस के लिए और विशेष रूप से फंक्शनल प्रोग्रामिंग के लिए काफी मौलिक है।

वाई कॉम्बीनेटर का दिन-प्रतिदिन व्यावहारिक मूल्य सीमित है, क्योंकि प्रोग्रामिंग भाषाएं आपको नाम देने देती हैं।

यदि आपको इसे पुलिस लाइनअप में पहचानने की आवश्यकता है, तो यह इस तरह दिखता है:

Y = λf। (Λx.f (xx)) (λx.f (xx))

आप आमतौर पर दोहराया होने के कारण इसे देख सकते हैं (λx.f (x x))

λप्रतीकों ग्रीक अक्षर लैम्ब्डा है, जो अपने नाम पथरी लैम्ब्डा देता हैं, और वहाँ की एक बहुत कुछ है (λx.t)शैली पद जो है, क्योंकि क्या लैम्ब्डा पथरी दिखेगा।


यह स्वीकृत उत्तर होना चाहिए। Btw, साथ U x = x x, Y = U . (. U)(कोस अंकन हास्केल की तरह)। IOW, उचित combinators के साथ, Y = BU(CBU)। इस प्रकार, Yf = U (f . U) = (f . U) (f . U) = f (U (f . U)) = f ((f . U) (f . U))
विल नेस 10

13

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

एक फिक्स्ड-पॉइंट कॉम्बिनेटर एक उच्च-क्रम फ़ंक्शन fixहै जो परिभाषा के अनुसार समतुल्यता को संतुष्ट करता है

forall f.  fix f  =  f (fix f)

fix fxनिश्चित-बिंदु समीकरण के समाधान का प्रतिनिधित्व करता है

               x  =  f x

एक प्राकृतिक संख्या के तथ्य को सिद्ध किया जा सकता है

fact 0 = 1
fact n = n * fact (n - 1)

fixसामान्य / μ-पुनरावर्ती कार्यों पर मनमाने ढंग से रचनात्मक प्रमाणों का उपयोग करना , बिना स्व-संदर्भ के प्राप्त किया जा सकता है।

fact n = (fix fact') n

कहाँ पे

fact' rec n = if n == 0
                then 1
                else n * rec (n - 1)

ऐसा है कि

   fact 3
=  (fix fact') 3
=  fact' (fix fact') 3
=  if 3 == 0 then 1 else 3 * (fix fact') (3 - 1)
=  3 * (fix fact') 2
=  3 * fact' (fix fact') 2
=  3 * if 2 == 0 then 1 else 2 * (fix fact') (2 - 1)
=  3 * 2 * (fix fact') 1
=  3 * 2 * fact' (fix fact') 1
=  3 * 2 * if 1 == 0 then 1 else 1 * (fix fact') (1 - 1)
=  3 * 2 * 1 * (fix fact') 0
=  3 * 2 * 1 * fact' (fix fact') 0
=  3 * 2 * 1 * if 0 == 0 then 1 else 0 * (fix fact') (0 - 1)
=  3 * 2 * 1 * 1
=  6

यह औपचारिक प्रमाण है कि

fact 3  =  6

विधिपूर्वक पुनर्लेखन के लिए नियत-बिंदु कॉम्बिनेटर तुल्यता का उपयोग करता है

fix fact'  ->  fact' (fix fact')

लैम्ब्डा कैलकुलस

Untyped लैम्ब्डा पथरी रीतिवाद एक विषय से मुक्त व्याकरण में होते हैं

E ::= v        Variable
   |  λ v. E   Abstraction
   |  E E      Application

बीटा और एटा कटौती नियमों के vसाथ, जहां चर पर पर्वतमाला होती है

(λ x. B) E  ->  B[x := E]                                 Beta
  λ x. E x  ->  E          if x doesn’t occur free in E   Eta

बीटा रिडक्शन xएक्सट्रैक्शन ("फंक्शन") बॉडी में वेरिएबल की सभी फ्री घटनाओं Bको एक्सप्रेशन ("वाद") द्वारा प्रतिस्थापित करता है E। एटा कमी निरर्थक अमूर्तता को समाप्त करती है। इसे कभी-कभी औपचारिकता से हटा दिया जाता है। एक विडंबनापूर्ण अभिव्यक्ति, जिसमें कोई कमी नियम लागू नहीं होती है, सामान्य या विहित रूप में होती है

λ x y. E

के लिए आशुलिपि है

λ x. λ y. E

(अमूर्त बहुआयामी),

E F G

के लिए आशुलिपि है

(E F) G

(आवेदन पत्र-संघात),

λ x. x

तथा

λ y. y

हैं अल्फा बराबर

अमूर्त और अनुप्रयोग लैम्ब्डा कैलकुलस के केवल दो "भाषा प्रधान" हैं, लेकिन वे मनमाने ढंग से जटिल डेटा और संचालन की एन्कोडिंग की अनुमति देते हैं ।

चर्च अंक पीनो-स्वयंसिद्ध नैचुरल के समान प्राकृतिक संख्याओं का एक एन्कोडिंग हैं।

   0  =  λ f x. x                 No application
   1  =  λ f x. f x               One application
   2  =  λ f x. f (f x)           Twofold
   3  =  λ f x. f (f (f x))       Threefold
    . . .

SUCC  =  λ n f x. f (n f x)       Successor
 ADD  =  λ n m f x. n f (m f x)   Addition
MULT  =  λ n m f x. n (m f) x     Multiplication
    . . .

एक औपचारिक प्रमाण है कि

1 + 2  =  3

बीटा कमी के पुनर्लेखन नियम का उपयोग करना:

   ADD                      1            2
=  (λ n m f x. n f (m f x)) (λ g y. g y) (λ h z. h (h z))
=  (λ m f x. (λ g y. g y) f (m f x)) (λ h z. h (h z))
=  (λ m f x. (λ y. f y) (m f x)) (λ h z. h (h z))
=  (λ m f x. f (m f x)) (λ h z. h (h z))
=  λ f x. f ((λ h z. h (h z)) f x)
=  λ f x. f ((λ z. f (f z)) x)
=  λ f x. f (f (f x))                                       Normal form
=  3

combinators

लैम्ब्डा कैलकुलस में, कॉम्बीनेटर अमूर्त होते हैं जिनमें कोई मुक्त चर नहीं होता है। ज्यादातर बस: Iपहचानकर्ता

λ x. x

पहचान समारोह के लिए isomorphic

id x = x

इस तरह के कॉम्बीनेटर एसकेआई सिस्टम की तरह कॉम्बीनेटर गणना के आदिम ऑपरेटर हैं ।

S  =  λ x y z. x z (y z)
K  =  λ x y. x
I  =  λ x. x

बीटा कमी दृढ़ता से सामान्य नहीं है ; नहीं सभी reducible भाव, "redexes", बीटा कमी के तहत सामान्य रूप में परिवर्तित। एक सरल उदाहरण ओमेगा ωकॉम्बिनेटर का विचलन अनुप्रयोग है

λ x. x x

खुद को:

   (λ x. x x) (λ y. y y)
=  (λ y. y y) (λ y. y y)
. . .
=  _|_                     Bottom

बाईं ओर के उप-विभाजनों ("हेड्स") को कम करना प्राथमिकता है। आवेदन आदेश प्रतिस्थापन से पहले तर्कों को सामान्य करता है , सामान्य आदेश नहीं करता है। दो रणनीतियाँ उत्सुक मूल्यांकन, उदा C, और आलसी मूल्यांकन, जैसे हास्केल के अनुरूप हैं।

   K          (I a)        (ω ω)
=  (λ k l. k) ((λ i. i) a) ((λ x. x x) (λ y. y y))

उत्सुक आवेदक-आदेश बीटा में कमी के तहत विचलन

=  (λ k l. k) a ((λ x. x x) (λ y. y y))
=  (λ l. a) ((λ x. x x) (λ y. y y))
=  (λ l. a) ((λ y. y y) (λ y. y y))
. . .
=  _|_

सख्त शब्दार्थों के बाद से

forall f.  f _|_  =  _|_

लेकिन आलसी सामान्य-क्रम बीटा कमी के तहत अभिसरण होता है

=  (λ l. ((λ i. i) a)) ((λ x. x x) (λ y. y y))
=  (λ l. a) ((λ x. x x) (λ y. y y))
=  a

यदि किसी अभिव्यक्ति का एक सामान्य रूप है, तो सामान्य-क्रम बीटा कमी उसे मिल जाएगी।

Y

फिक्स्ड-पॉइंट कॉम्बिनेटर की आवश्यक संपत्तिY

λ f. (λ x. f (x x)) (λ x. f (x x))

द्वारा दिया गया है

   Y g
=  (λ f. (λ x. f (x x)) (λ x. f (x x))) g
=  (λ x. g (x x)) (λ x. g (x x))           =  Y g
=  g ((λ x. g (x x)) (λ x. g (x x)))       =  g (Y g)
=  g (g ((λ x. g (x x)) (λ x. g (x x))))   =  g (g (Y g))
. . .                                      . . .

समतुल्यता

Y g  =  g (Y g)

isomorphic to है

fix f  =  f (fix f)

लावारिस लैम्ब्डा कैलकुलस सामान्य / μ-पुनरावर्ती कार्यों पर मनमाने ढंग से रचनात्मक साक्ष्यों को सांकेतिक कर सकता है।

 FACT  =  λ n. Y FACT' n
FACT'  =  λ rec n. if n == 0 then 1 else n * rec (n - 1)

   FACT 3
=  (λ n. Y FACT' n) 3
=  Y FACT' 3
=  FACT' (Y FACT') 3
=  if 3 == 0 then 1 else 3 * (Y FACT') (3 - 1)
=  3 * (Y FACT') (3 - 1)
=  3 * FACT' (Y FACT') 2
=  3 * if 2 == 0 then 1 else 2 * (Y FACT') (2 - 1)
=  3 * 2 * (Y FACT') 1
=  3 * 2 * FACT' (Y FACT') 1
=  3 * 2 * if 1 == 0 then 1 else 1 * (Y FACT') (1 - 1)
=  3 * 2 * 1 * (Y FACT') 0
=  3 * 2 * 1 * FACT' (Y FACT') 0
=  3 * 2 * 1 * if 0 == 0 then 1 else 0 * (Y FACT') (0 - 1)
=  3 * 2 * 1 * 1
=  6

(गुणा विलम्ब, संगम)

चर्चियन अनपिप्ड लैम्ब्डा कैलकुलस के लिए, इसके अलावा फिक्स्ड-पॉइंट कॉम्बिनेटरों की एक पुनरावृत्ति करने योग्य अनन्तता का अस्तित्व दिखाया गया है Y

 X  =  λ f. (λ x. x x) (λ x. f (x x))
Y'  =  (λ x y. x y x) (λ y x. y (x y x))
 Z  =  λ f. (λ x. f (λ v. x x v)) (λ x. f (λ v. x x v))
 Θ  =  (λ x y. y (x x y)) (λ x y. y (x x y))
  . . .

सामान्य-क्रम बीटा में कमी से अपरिचित लाम्बडा कैलकुलस को ट्यूरिंग-पूर्ण रीराइट सिस्टम बना देता है।

हास्केल में, फिक्स्ड-पॉइंट कॉम्बिनेटर को सुरुचिपूर्ण ढंग से लागू किया जा सकता है

fix :: forall t. (t -> t) -> t
fix f = f (fix f)

हास्केल का आलस्य एक सूक्ष्मता को सामान्य करता है, इससे पहले कि सभी उपप्रकारों का मूल्यांकन किया गया हो।

primes :: Integral t => [t]
primes = sieve [2 ..]
   where
      sieve = fix (\ rec (p : ns) ->
                     p : rec [n | n <- ns
                                , n `rem` p /= 0])


4
हालांकि, मैं उत्तर की संपूर्णता की सराहना करता हूं, यह प्रोग्रामर के लिए पहली पंक्ति के विराम के बाद थोड़ी औपचारिक गणित पृष्ठभूमि के साथ किसी भी तरह से स्वीकार्य नहीं है।
जेरेड स्मिथ

4
@ jared-smith इसका उत्तर वाई कॉम्बीनेटर के पीछे CS / गणित की धारणाओं के बारे में एक पूरक वोनकेन कहानी बताने के लिए है। मुझे लगता है कि, शायद, परिचित अवधारणाओं के लिए सबसे अच्छा संभव एनालॉग अन्य उत्तरदाताओं द्वारा पहले से ही तैयार किए गए हैं। निजी तौर पर, मुझे हमेशा एक अच्छा सादृश्य से अधिक, एक मूल विचार, वास्तविक विचार की मौलिक नवीनता के साथ सामना करना पसंद है । मैं सबसे व्यापक उपमाओं को अनुचित और भ्रामक पाता हूं।

1
नमस्कार, आइडेंटिटी कॉम्बिनेटर λ x . x, आज आप कैसे हैं?
माईविक्टर

मुझे यह जवाब सबसे ज्यादा पसंद है । यह मेरे सभी सवालों को साफ कर दिया!
छात्र

11

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


6

यहाँ Y-Combinator और Factorial फ़ंक्शन (डगलस क्रॉकफ़ोर्ड के लेख से, http://javascript.crockford.com/little.html पर उपलब्ध ) का जावास्क्रिप्ट कार्यान्वयन है ।

function Y(le) {
    return (function (f) {
        return f(f);
    }(function (f) {
        return le(function (x) {
            return f(f)(x);
        });
    }));
}

var factorial = Y(function (fac) {
    return function (n) {
        return n <= 2 ? n : n * fac(n - 1);
    };
});

var number120 = factorial(5);

6

वाई-कॉम्बिनेटर एक फ्लक्स कैपेसिटर का दूसरा नाम है।


4
बहुत अजीब बात है। :) युवा (एर) वाले संदर्भ को नहीं पहचान सकते हैं।
विल नेस

2
haha! हां, युवा एक (मुझे) अभी भी समझ सकता है ...

मुझे लगा कि यह वास्तविक है और मैं यहीं समाप्त हो गया। youtube.com/watch?v=HyWqxkaQpPw हाल अनुच्छेद futurism.com/scientists-made-real-life-flux-capacitor
देखा Thinkar अस्वीकार Htoo

मुझे लगता है कि यह उत्तर गैर-अंग्रेजी बोलने वालों के लिए विशेष रूप से भ्रमित करने वाला हो सकता है। इस दावे को समझने से पहले (या कभी नहीं) यह समझने के लिए कि यह एक विनोदी लोकप्रिय संस्कृति संदर्भ है, को काफी समय दे सकते हैं। (मुझे यह पसंद है, मुझे बस बुरा लगेगा अगर मैंने इसका जवाब दिया और पता चला कि किसी को सीखने से हतोत्साहित किया गया था)
माइक

5

मैंने क्लोजर और स्कीम दोनों में वाई-कॉम्बिनेटर को एक तरह का "इडियट्स गाइड" लिखा है ताकि खुद को इससे ग्रिप करने में मदद कर सकूं। वे "द लिटिल स्कीमर" में सामग्री से प्रभावित हैं

स्कीम में: https://gist.github.com/z5h/238891

or Clojure: https://gist.github.com/z5h/5102747

दोनों ट्यूटोरियल टिप्पणियों के साथ कूट-कूट कर भरे हुए हैं और आपके पसंदीदा संपादक में कट और पेस्ट करने योग्य होने चाहिए।


5

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

से भद्दा को कम भद्दा

एक उदाहरण के रूप में भाज्य का उपयोग करना, हम almost-factorialसंख्या के भाज्य की गणना करने के लिए निम्नलिखित फ़ंक्शन का उपयोग करते हैं x:

def almost-factorial f x = if iszero x
                           then 1
                           else * x (f (- x 1))

ऊपर छद्म कोड almost-factorialमें, फ़ंक्शन fऔर संख्या में होता है x( almost-factorialकरी जाती है, इसलिए इसे फ़ंक्शन में लेने के रूप में देखा जा सकता हैf और 1-एरिटी फ़ंक्शन को वापस करने) के ।

जब के almost-factorialलिए factorial की गणना करता है x, यह x - 1कार्य करने के लिए factorial की गणना को दर्शाता है fऔर उस परिणाम को जमा करता हैx (इस मामले में, यह x के साथ (x - 1) के परिणाम को गुणा करता है)।

इसे फैक्टरियल फंक्शन के almost-factorialएक भद्दे संस्करण के रूप में देखा जा सकता है (जो केवल संख्या तक गणना कर सकता है x - 1) और फैक्टरियल के कम-गंदे संस्करण (जो संख्या तक की गणना करता है ) को लौटाता है x। इस रूप में:

almost-factorial crappy-f = less-crappy-f

यदि हम बार-बार फैक्टरियल के कम-गंदे संस्करण को पास करते हैं almost-factorial, तो हम अंततः अपने वांछित फैक्टरियल फ़ंक्शन को प्राप्त करेंगे f। जहां इसे माना जा सकता है:

almost-factorial f = f

फिक्स सूत्री

तथ्य यह है कि almost-factorial f = fइसका मतलब fहै ठीक सूत्री समारोह केalmost-factorial

उपरोक्त कार्यों के संबंधों को देखने का यह वास्तव में एक दिलचस्प तरीका था और यह मेरे लिए एक महत्वपूर्ण क्षण था। (यदि आपने नहीं किया है तो कृपया फिक्स-पॉइंट पर माइक की पोस्ट पढ़ें)

तीन कार्य

सामान्य बनाने के लिए, हमारे पास एक गैर-पुनरावर्ती फ़ंक्शन fn(जैसे हमारे लगभग-फैक्टरियल) है, हमारे पास इसके फिक्स-पॉइंट फ़ंक्शन fr(जैसे हमारे एफ) हैं, फिर Yजब आप देते हैं तो क्या होता है Y fn, Yफिक्स-पॉइंट फ़ंक्शन को वापस करता है fn

तो सारांश में (यह मानते हुए द्वारा सरल बनाया fr, केवल एक पैरामीटर लेता xकरने के लिए घिनौना x - 1, x - 2: ... प्रत्यावर्तन में)

  • हम मुख्य गणना को इस रूप में परिभाषित करते हैं fn: def fn fr x = ...accumulate x with result from (fr (- x 1))यह लगभग उपयोगी कार्य है - हालांकि हम fnसीधे उपयोग नहीं कर सकते हैं x, यह बहुत जल्द उपयोगी होगा। यह गैर-पुनरावर्ती अपने परिणाम की गणना करने के लिए fnएक फ़ंक्शन का उपयोग करता हैfr
  • fn fr = fr, frका ठीक-बिंदु है fn, frहै उपयोगी funciton, हम उपयोग कर सकते हैं frपर xहमारे परिणाम प्राप्त करने के
  • Y fn = fr, Yएक फ़ंक्शन के फिक्स-पॉइंट को लौटाता है, Y हमारे लगभग-उपयोगी फ़ंक्शन fnको उपयोगी बनाता है fr

व्युत्पन्न Y(शामिल नहीं)

मैं की व्युत्पत्ति को छोड़ दूंगा Yऔर समझ जाऊंगा Y। माइक वैनर की पोस्ट में बहुत सारी जानकारियां हैं।

का रूप Y

Yके रूप में परिभाषित किया गया है ( लैम्ब्डा कैलकुलस प्रारूप में):

Y f = λs.(f (s s)) λs.(f (s s))

यदि हम sकार्यों के बाईं ओर चर की जगह लेते हैं , तो हम प्राप्त करते हैं

Y f = λs.(f (s s)) λs.(f (s s))
=> f (λs.(f (s s)) λs.(f (s s)))
=> f (Y f)

तो वास्तव में, का परिणाम (Y f)तय बिंदु है f

(Y f)काम क्यों करता है ?

के हस्ताक्षर के आधार f, (Y f)सरल करने के लिए, किसी भी arity के एक समारोह हो सकता है, मान लेते हैं करते हैं (Y f)केवल हमारे भाज्य समारोह की तरह एक पैरामीटर लेता है,।

def fn fr x = accumulate x (fr (- x 1))

तब से fn fr = fr, हम जारी रखते हैं

=> accumulate x (fn fr (- x 1))
=> accumulate x (accumulate (- x 1) (fr (- x 2)))
=> accumulate x (accumulate (- x 1) (accumulate (- x 2) ... (fn fr 1)))

पुनरावर्ती गणना समाप्त हो जाती है जब आंतरिक- (fn fr 1)आधार आधार मामला है और गणना में fnउपयोग नहीं करता है fr

Yफिर से देखना:

fr = Y fn = λs.(fn (s s)) λs.(fn (s s))
=> fn (λs.(fn (s s)) λs.(fn (s s)))

इसलिए

fr x = Y fn x = fn (λs.(fn (s s)) λs.(fn (s s))) x

मेरे लिए, इस सेटअप के जादुई हिस्से हैं:

  • fnऔर frएक दूसरे पर interdepend: fr'wraps' fnअंदर, हर बार frकी गणना करने के लिए किया जाता है x, यह 'spawns' (? 'लिफ्टों') एक fnऔर प्रतिनिधियों कि करने के लिए गणना fn(अपने आप में गुजर frऔर x); दूसरी ओर, एक छोटी सी समस्या के परिणाम की गणना करने fnपर निर्भर करता है frऔर उपयोग करता frहैx-1
  • समय frका उपयोग परिभाषित करने के लिए किया जाता है fn(जब इसके संचालन में fnउपयोग होता frहै), वास्तविकfr अभी तक परिभाषित नहीं है।
  • यह fnवास्तविक व्यापार तर्क को परिभाषित करता है। के आधार पर fn, Yबनाता है fr- एक विशिष्ट रूप में एक सहायक समारोह - के लिए गणना की सुविधा के लिए fnएक में पुनरावर्ती तरीके।

इसने मुझे Yइस समय इस तरह समझने में मदद की , आशा है कि यह मदद करेगा।

BTW, मैं भी एक किताब के लिए एक परिचय प्रोग्रामिंग के माध्यम से लैम्ब्डा पथरी के माध्यम से बहुत अच्छा मिला, मैं केवल इसके माध्यम से हिस्सा है और तथ्य यह है कि मैं Yकिताब में मेरे सिर के आसपास नहीं मिल सका मुझे इस पद के लिए नेतृत्व किया।


5

यहाँ मूल प्रश्नों के उत्तर दिए गए हैं , जो लेख से संकलित हैं (जो कि पढ़ने लायक है) निकोलस मंचुसो के उत्तर में उल्लिखित है , साथ ही साथ अन्य उत्तर भी हैं:

वाई-कॉम्बिनेटर क्या है?

एक वाई-कॉम्बिनेटर एक "कार्यात्मक" (या एक उच्च-क्रम फ़ंक्शन - एक फ़ंक्शन जो अन्य कार्यों पर काम करता है) एक एकल तर्क लेता है, जो एक फ़ंक्शन है जो पुनरावर्ती नहीं है, और फ़ंक्शन का एक संस्करण लौटाता है पुनरावर्ती।


थोड़ा पुनरावर्ती =), लेकिन अधिक गहराई से परिभाषा:

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

इसके बारे में सोचने का एक और तरीका यह है कि कॉम्बीनेटर एक लैम्ब्डा एक्सप्रेशन है, जिसमें आप एक कॉम्बिनेटर का नाम अपनी परिभाषा के साथ बदल सकते हैं, जो कि हर जगह पाया जाता है और इसमें अभी भी सब कुछ काम है (आप एक अनंत लूप में मिलेंगे, यदि कॉम्पेबेटर होगा) अपने आप को संदर्भ दें, लंबोदर शरीर के अंदर)।

वाई-कॉम्बिनेटर एक फिक्स्ड-पॉइंट कॉम्बिनेटर है।

फ़ंक्शन का निश्चित बिंदु फ़ंक्शन के डोमेन का एक तत्व है जिसे फ़ंक्शन द्वारा स्वयं मैप किया जाता है।
कहने का तात्पर्य यह है कि यदि यह अर्थ है तो cफ़ंक्शन का एक निश्चित बिंदु हैf(x)f(c) = c
f(f(...f(c)...)) = fn(c) = c

कॉम्बिनेटर कैसे काम करते हैं?

नीचे दिए गए उदाहरण मजबूत + गतिशील टाइपिंग मानते हैं :

आलसी (सामान्य-क्रम) Y-combinator:
यह परिभाषा आलसी (भी: आस्थगित, कॉल-बाय-नीड) मूल्यांकन वाली भाषाओं पर लागू होती है - मूल्यांकन रणनीति जो किसी अभिव्यक्ति के मूल्यांकन को तब तक विलंबित करती है जब तक उसके मूल्य की आवश्यकता नहीं होती।

Y = λf.(λx.f(x x)) (λx.f(x x)) = λf.(λx.(x x)) (λx.f(x x))

इसका मतलब यह है कि, किसी दिए गए फ़ंक्शन f(जो कि एक गैर-पुनरावर्ती कार्य है) के लिए, संगत पुनरावर्ती फ़ंक्शन को पहले कंप्यूटिंग द्वारा प्राप्त किया जा सकता है λx.f(x x), और फिर इस लंबो एक्सप्रेशन को खुद पर लागू किया जा सकता है।

सख्त (अनुप्रयोग-क्रम) Y-combinator:
यह परिभाषा सख्त (भी: उत्सुक, लालची) मूल्यांकन के साथ भाषाओं पर लागू होती है - मूल्यांकन रणनीति जिसमें एक चर के लिए बाध्य होते ही एक अभिव्यक्ति का मूल्यांकन किया जाता है।

Y = λf.(λx.f(λy.((x x) y))) (λx.f(λy.((x x) y))) = λf.(λx.(x x)) (λx.f(λy.((x x) y)))

यह प्रकृति में आलसी के समान है, λलैंबडा के शरीर के मूल्यांकन में देरी करने के लिए इसके पास अतिरिक्त आवरण है। मैंने एक और प्रश्न पूछा है , जो इस विषय से संबंधित है।

वे किस चीज़ लिए अच्छे हैं?

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

हालांकि, वाई-कॉम्बिनेटर के पास कुछ व्यावहारिक अनुप्रयोग हैं, यह मुख्य रूप से एक सैद्धांतिक अवधारणा है, जिसकी समझ आपके समग्र दृष्टिकोण और इच्छाशक्ति, आपके विश्लेषणात्मक और डेवलपर कौशल को बढ़ाएगी।

क्या वे प्रक्रियात्मक भाषाओं में उपयोगी हैं?

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

और जैसा कि क्रिस अम्मरमन ने उल्लेख किया है : अधिकांश प्रक्रियात्मक भाषाओं में स्थैतिक-टाइपिंग होती है।

तो इस एक का जवाब - वास्तव में नहीं।


4

Y- कॉम्बिनेटर अनाम पुनरावृत्ति को लागू करता है। इसलिए इसके बजाय

function fib( n ){ if( n<=1 ) return n; else return fib(n-1)+fib(n-2) }

तुम कर सकते हो

function ( fib, n ){ if( n<=1 ) return n; else return fib(n-1)+fib(n-2) }

बेशक, वाई-कॉम्बिनेटर केवल कॉल-बाय-नाम भाषाओं में काम करता है। यदि आप इसे किसी भी सामान्य कॉल-बाय-वैल्यू भाषा में उपयोग करना चाहते हैं, तो आपको संबंधित z- कॉम्बीनेटर (y- कॉम्बीनेटर डायवर्ज / अनंत-लूप) की आवश्यकता होगी।


वाई कॉम्बिनेटर पास-बाय-वैल्यू और आलसी मूल्यांकन के साथ काम कर सकता है।
क्लेक्लेफ़

3

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


3

यह ऑपरेटर आपके जीवन को आसान बना सकता है:

var Y = function(f) {
    return (function(g) {
        return g(g);
    })(function(h) {
        return function() {
            return f.apply(h(h), arguments);
        };
    });
};

फिर आप अतिरिक्त कार्य से बचें:

var fac = Y(function(n) {
    return n == 0 ? 1 : n * this(n - 1);
});

अंत में, आप कॉल करें fac(5)


0

मुझे लगता है कि इसका जवाब देने का सबसे अच्छा तरीका है कि आप भाषा चुनें, जैसे जावास्क्रिप्ट:

function factorial(num)
{
    // If the number is less than 0, reject it.
    if (num < 0) {
        return -1;
    }
    // If the number is 0, its factorial is 1.
    else if (num == 0) {
        return 1;
    }
    // Otherwise, call this recursive procedure again.
    else {
        return (num * factorial(num - 1));
    }
}

अब इसे फिर से लिखें ताकि यह फ़ंक्शन के अंदर फ़ंक्शन के नाम का उपयोग न करे, लेकिन फिर भी इसे पुनरावर्ती कहता है।

फ़ंक्शन नाम का एकमात्र स्थान factorialकॉल साइट पर देखा जाना चाहिए।

संकेत: आप फ़ंक्शंस के नामों का उपयोग नहीं कर सकते, लेकिन आप मापदंडों के नामों का उपयोग कर सकते हैं।

समस्या का काम करें। इसे मत देखो। एक बार जब आप इसे हल कर लेते हैं, तो आप समझ जाएंगे कि वाई-कॉम्बिनेटर क्या समस्या हल करता है।


1
क्या आप सुनिश्चित हैं कि यह हल करने की तुलना में अधिक समस्याएं पैदा नहीं करता है?
नोक्टिस स्काईटॉवर

1
नोक्टिस, क्या आप अपना प्रश्न स्पष्ट कर सकते हैं? क्या आप पूछ रहे हैं कि क्या y- कॉम्बीनेटर की अवधारणा खुद को हल करने की तुलना में अधिक समस्याएं पैदा करती है, या आप विशेष रूप से बात कर रहे हैं कि मैंने विशेष रूप से जावास्क्रिप्ट का उपयोग करके प्रदर्शित करने के लिए चुना, या मेरे विशिष्ट कार्यान्वयन या इसे स्वयं की खोज करके इसे सीखने की मेरी सिफारिश। मैंने वर्णन किया?
5
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.