1 नामहीन फ़ंक्शन की सारणी
1.1 नामहीन फ़ंक्शन के सिद्धांतिक परिचय
नामहीन फ़ंक्शन वह फ़ंक्शन होते हैं जिनका कोई स्पष्ट नाम नहीं होता है। इन्हें सीधे परिभाषित और उपयोग किया जा सकता है जब एक फ़ंक्शन प्रकार की आवश्यकता होती है। ऐसे फ़ंक्शनों का उपयोग स्थानीय एन्कैप्सुलेशन को कार्यान्वित करने या छोटे अवधि वाले संदर्भों में किया जाता है। उनकी तुलना में नामित फ़ंक्शनों के साथ, नामहीन फ़ंक्शनों को किसी नाम की आवश्यकता नहीं होती है, जिससे इसे एक चर में परिभाषित किया जा सकता है या इस्तेमाल किया जा सकता है।
1.2 नामहीन फ़ंक्शनों की परिभाषा और उपयोग
गो भाषा में, नामहीन फ़ंक्शन की परिभाषा के लिए मूलभूत वाक्यप्रणाली निम्नलिखित है:
func(आर्गुमेंट) {
// फ़ंक्शन का शरीर
}
नामहीन फ़ंक्शनों का उपयोग दो मामलों में किया जा सकता है: एक चर में सौंपा जाना या सीधा निष्पादन।
- किसी चर में सौंपा जाता है:
योग := func(a int, b int) int {
return a + b
}
परिणाम := योग(3, 4)
fmt.Println(परिणाम) // आउटपुट: 7
इस उदाहरण में, नामहीन फ़ंक्शन को चर योग
को सौंपा गया है, और फिर हम योग
को एक साधारण फ़ंक्शन की तरह कॉल करते हैं।
- सीधा निष्पादन (जिसे स्वत: निष्पादी नामहीन फ़ंक्शन कहा जाता है):
func(a int, b int) {
fmt.Println(a + b)
}(3, 4) // आउटपुट: 7
इस उदाहरण में, नामहीन फ़ंक्शन को परिभाषित होने के तुरंत बाद स्वत: निष्पादित किया जाता है, किसी भी चर को सौंपने की आवश्यकता नहीं होती है।
1.3 नामहीन फ़ंक्शन अनुप्रयोगों के वास्तविक उदाहरण
नामहीन फ़ंक्शनों का गो भाषा में व्यापक रूप से उपयोग होता है, और यहां कुछ सामान्य प्रकारियों की कुछ सामान्य स्थितियाँ हैं:
- कॉलबैक फ़ंक्शन के रूप में: नामहीन फ़ंक्शन सामान्यत: कॉलबैक तर्क को कार्यान्वित करने के लिए प्रयोग किया जाता है। उदाहरण के लिए, जब एक फ़ंक्शन दूसरे फ़ंक्शन को पैरामीटर के रूप में लेता है, तो आप एक नामहीन फ़ंक्शन को पास कर सकते हैं।
func traverse(numbers []int, callback func(int)) {
for _, num := range numbers {
callback(num)
}
}
traverse([]int{1, 2, 3}, func(n int) {
fmt.Println(n * n)
})
इस उदाहरण में, नामहीन फ़ंक्शन को traverse
के कॉलबैक पैरामीटर के रूप में पास किया गया है, और प्रत्येक संख्या को वर्गों में प्रिंट किया जाता है।
- तुरंत निष्पादन कार्यों के लिए: कभी-कभी, हमें एक फ़ंक्शन को केवल एक बार निष्पादित किया जाना चाहिए और निष्पादन स्थान निकट है। इस आवश्यकता को पूरा करने के लिए और कोड की पुनरावृत्ति कम करने के लिए नामहीन फ़ंक्शनों को तुरंत कॉल किया जा सकता है।
func main() {
// ...अन्य कोड...
// जो कोड तुरंत निष्पादित किया जाना चाहिए
func() {
// कार्य निष्पादन के लिए कोड
fmt.Println("तुरंत नामहीन फ़ंक्शन निष्पादित हुआ।")
}()
}
यहां, नामहीन फ़ंक्शन घोषणा के बाद तुरंत निष्पादित होता है, छोटे कार्य को परिभाषित करने के लिए बाहरी फ़ंक्शन को परिभाषित करने की आवश्यकता नहीं होती है।
- बंद: नामहीन फ़ंक्शन सामान्यत: बंदिशें बनाने के लिए प्रयोग किए जाते हैं क्योंकि वे बाहरी चरों को कैप्चर कर सकते हैं।
func sequenceGenerator() func() int {
i := 0
return func() int {
i++
return i
}
}
इस उदाहरण में, sequenceGenerator
एक नामहीन फ़ंक्शन लौटाता है जो परिभाषित चर i
पर बंद होता है, और प्रत्येक कॉल i
को इंक्रीमेंट करेगा।
यह स्पष्ट है कि नामहीन फ़ंक्शनों की लचीलाई वास्तविक प्रोग्रामिंग में महत्वपूर्ण भूमिका निभाती है, जो कोड को सरल बनाती है और पठनीयता में सुधार करती है। आगामी अनुभागों में, हम बंदिशों पर विस्तार से चर्चा करेंगे, जिसमें उनकी विशेषताएँ और अनुप्रयोग शामिल है।
2 बंदिशों की गहरी समझ
2.1 बंदिश की अवधारणा
एक बंदिश एक फ़ंक्शन मूल्य होता है जो इसके फ़ंक्शन शरीर के बाहर के चरों पर संदर्भित करता है। यह फ़ंक्शन इन चरों का उपयोग कर सकता है और बाइंड कर सकता है, जिसका मतलब है कि इसे न केवल इन चरों का उपयोग करने की अनुमति होती है बल्कि यह इन चरों को संशोधित भी कर सकता है। बंदिश अनामित फ़ंक्शनों के साथ अक्सर जुड़ी होती है, क्योंकि अनामित फ़ंक्शनों का अपना नाम नहीं होता और वे अक्सर वेरू जहां जरूरत होती है वहां सीधे परिभाषित होते हैं, बंदिशों के लिए ऐसे माहौल का निर्माण करते हैं।
बंदिश की अवधारणा को प्रमाण के परिवेश और विस्तार से अलग नहीं किया जा सकता है। गो भाषा में, प्रत्येक फ़ंक्शन के आपसी स्तंभ होते हैं, जो फ़ंक्शन के स्थानीय चरों को संदर्भित करते हैं। हालांकि, जब फ़ंक्शन वापस लौटता है, तो उसका स्टैक फ्रेम अब मौजूद नहीं होता है। बंदिशों की जादू उस बात में है कि बाहरी फ़ंक्शन वापस लौटने के बाद भी, बंदिश अभी भी बाहरी फ़ंक्शन के चरों का संदर्भ कर सकती है।
func outer() func() int {
count := 0
return func() int {
count += 1
return count
}
}
func main() {
closure := outer()
println(closure()) // आउटपुट: 1
println(closure()) // आउटपुट: 2
}
इस उदाहरण में, outer
फ़ंक्शन एक बंदिश लौटाता है जो चर count
का संदर्भ करती है। बाहरी फ़ंक्शन के निष्पादन के बाद भी, बंदिश अभी भी count
को संशोधित कर सकती है।
2.2 अनामत फंक्शन के साथ संबंध
अनामत फंक्शन और क्लोजर्स का गहरा संबंध है। Go भाषा में, अनामत फंक्शन एक एसी फंक्शन होता है जिसका कोई नाम नहीं होता और इसे तुरंत परिभाषित किया और उपयोग किया जा सकता है जब चाहें। इस प्रकार के फंक्शन में क्लोजर्स का व्यवहार लागू करने के लिए खास रूप से उपयुक्त होता है।
क्लोजर्स आमतौर पर अनामत फंक्शन के भीतर कार्यान्वित होते हैं, जो अपने आसपास की स्कोप से चर वेरिएबल को कैप्चर कर सकते हैं। जब अनामत फंक्शन एक बाहरी स्कोप से वेरिएबल का संदर्भ करता है, तो अनामत फंक्शन के साथ संदर्भित चर वेरिएबल एक क्लोजर बनाते हैं।
func main() {
adder := func(sum int) func(int) int {
return func(x int) int {
sum += x
return sum
}
}
sumFunc := adder()
println(sumFunc(2)) // Output: 2
println(sumFunc(3)) // Output: 5
println(sumFunc(4)) // Output: 9
}
यहां, फंक्शन adder
एक अनामत फंक्शन वापस करता है, जो वेरिएबल sum
का संदर्भ करके एक क्लोजर बनाता है।
2.3 क्लोजर्स की विशेषताएँ
क्लोजर्स की सबसे स्पष्ट विशेषता उनकी क्षमता है कि वे याद रख सकते हैं वहां के वातावरण को जहां उन्होंने निर्मित किए थे। वे अपने खुद के फंक्शन के बाहर परिभाषित चरों तक पहुँच सकते हैं। क्लोजर्स की प्राकृतिक गुणवत्ता उन्हें अपने आप में स्थिति (बाहरी चरों को संदर्भित करके) संलिपन देने की संकेत में बहुत कई शक्तिशाली विशेषताओं के लिए बुनियाद प्रदान करती है। जैसे कि डिकोरेटर, स्थिति संलिपन और आलस्य मूल्यांकन जैसी कई प्रोग्रामिंग की शक्तिशाली विशेषताओं को कार्यान्वित करने के लिए।
स्थिति संलिपन के अलावा, क्लोजर्स की निम्नलिखित विशेषताएँ होती हैं:
- चरों के आयु को बढ़ाना: क्लोजर्स द्वारा संदर्भित बाहरी चरों की आयु का समय क्लोजर के अस्तित्व के पूरे अवधि तक बढ़ जाता है।
- निजी चरों का संलिपन: अन्य विधियाँ सीधे क्लोजर्स के आंतरिक चरों तक सीधे पहुँच नहीं प्रदान कर सकती हैं, जिससे निजी चरों को संलिपित करने का एक साधन प्रदान किया जाता है।
2.4 सामान्य गलतियाँ और विचार
क्लोजर्स का उपयोग करते समय, कुछ सामान्य गलतियों और बिंदुओं को ध्यान रखना चाहिए:
- लूप चर के बाइंडिंग समस्या: लूप के भीतर चर का उपयोग सीधे एक क्लोजर बनाने में समस्या उत्पन्न कर सकता है क्योंकि प्रत्येक चक्रवात में चर का पता नहीं बदलता है।
for i := 0; i < 3; i++ {
defer func() {
println(i)
}()
}
// The output may not be the expected 0, 1, 2, but 3, 3, 3
इस गलती से बचने के लिए, चक्रवात चर को क्लोजर को एक पैरामीटर के रूप में पारित किया जाना चाहिए:
for i := 0; i < 3; i++ {
defer func(i int) {
println(i)
}(i)
}
// Correct output: 0, 1, 2
-
क्लोजर मेमोरी लीक: अगर किसी क्लोजर में एक बड़े स्थानीय चर का संदर्भ है और इस क्लोजर को लंबे समय तक रखा जाता है, तो स्थानीय चर को नहीं रिकवर किया जाएगा, जिससे मेमोरी लीक हो सकती है।
-
क्लोजर में साथीता समस्याएँ: अगर किसी क्लोजर को साथीता से चलाया गया है और किसी चर का संदर्भ है, तो इसे सुनिश्चित करना चाहिए कि यह संदर्भ साथीता-सुरक्षित है। सामान्यत: इसे सिंक्रोनाइज़ेशन प्रायोगिकी जैसे कि म्यूटेक्स लॉक की आवश्यकता होती है।
इन गलतियों और विचारों को समझने से डेवलपर्स को क्लोजर्स का अधिक सुरक्षित और प्रभावी उपयोग करने में मदद मिल सकती है।