यू कोम्बिनेटर
एक फ़ंक्शन को एक तर्क के रूप में पारित करके, एक फ़ंक्शन अपने नाम के बजाय अपने पैरामीटर का उपयोग करके पुनरावृत्ति कर सकता है! तो दिए गए फ़ंक्शन 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)
गुमनाम कार्यों के अलावा और कुछ नहीं का उपयोग करके गणना की गई