Go में फंक्शन फर्स्ट-क्लास वैल्यू होते हैं। इसका मतलब है कि फंक्शन के साथ आप वही सब कुछ कर सकते हैं जो आप दूसरी सारी वैल्यू के साथ कर सकते हैं: फंक्शन को वेरिएबल में असाइन करना, उन्हें आर्गुमेंट के रूप में दूसरे फंक्शन को देना, या किसी फंक्शन से कोई फंक्शन लौटाना।
नीचे हम दो फंक्शन बना रहे हैं, engGreeting और espGreeting, और उन्हें greeting वेरिएबल में असाइन कर रहे हैं:
import "fmt"
func engGreeting(name string) string {
return fmt.Sprintf("Hello %s, nice to meet you!", name)
}
func espGreeting(name string) string {
return fmt.Sprintf("¡Hola %s, mucho gusto!", name)
}
greeting := engGreeting // greeting is a variable of type func(string) string
fmt.Println(greeting("Alice")) // Hello Alice, nice to meet you!
greeting = espGreeting
fmt.Println(greeting("Alice")) // ¡Hola Alice, mucho gusto!
फंक्शन वैल्यू की मदद से हम फंक्शन को सिर्फ डेटा ही नहीं, बल्कि व्यवहार भी पैरामीटर की तरह दे सकते हैं।
अगले उदाहरण में हम greetingFunc पैरामीटर के ज़रिए dialog फंक्शन को व्यवहार दे रहे हैं:
func dialog(name string, greetingFunc func(string) string) {
fmt.Println(greetingFunc(name))
fmt.Println("I'm a dialog bot.")
}
func espGreeting(name string) string {
return fmt.Sprintf("¡Hola %s, mucho gusto!", name)
}
greeting := espGreeting
dialog("Alice", greeting)
// =>
// ¡Hola Alice, mucho gusto!
// I'm a dialog bot.
फंक्शन टाइप के जिस वेरिएबल को कोई शुरुआती वैल्यू नहीं दी गई हो, उसकी वैल्यू nil होती है।
इसलिए nil फंक्शन वैल्यू को कॉल करने पर पैनिक हो जाता है।
var dutchGreeting func(string) string
dutchGreeting("Alice") // panic: call of nil function
फंक्शन वैल्यू की तुलना nil से की जा सकती है।
यह प्रोग्राम में बेवजह होने वाले पैनिक से बचने में काम आ सकता है।
var dutchGreeting func(string) string
if dutchGreeting != nil {
dutchGreeting("Alice") // safe to call dutchGreeting
}
Go में फंक्शन वैल्यू का इस्तेमाल फंक्शन टाइप की मदद से संभव होता है।
फंक्शन टाइप उन सारे फंक्शन के समूह को दर्शाता है जिनके पैरामीटर टाइप और परिणाम टाइप एक ही क्रम में होते हैं।
फंक्शन टाइप के ऊपर यूज़र-डिफाइंड टाइप घोषित किए जा सकते हैं।
जैसे, पिछले उदाहरणों वाले dialog फंक्शन को इस तरह बदला जा सकता है:
type greetingFunc func(string) string
func dialog(name string, f greetingFunc) {
fmt.Println(f(name))
fmt.Println("I'm a dialog bot.")
}
फर्स्ट-क्लास फंक्शन के समर्थन से एक और बहुत काम का साधन उपलब्ध होता है, जिसे अनाम फंक्शन कहते हैं।
अनाम फंक्शन को उसी जगह बनाया जाता है जहाँ उसका इस्तेमाल होता है, और func कीवर्ड के बाद कोई नाम नहीं लिखा जाता।
ऐसे फंक्शन उस फंक्शन के वेरिएबल तक पहुँच सकते हैं जिसके अंदर वे बनाए गए हैं।
उदाहरण के लिए:
func fib() func() int {
var n1, n2 int
return func() int {
if n1 == 0 && n2 == 0 {
n1 = 1
} else {
n1, n2 = n2, n1 + n2
}
return n2
}
}
next := fib()
for i := range N {
fmt.Printf("F%d\t= %4d\n", i, next())
}
fib को कॉल करने पर वेरिएबल n1 और n2 घोषित होते हैं और एक अनाम फंक्शन लौटता है। वह अनाम फंक्शन हर बार कॉल किए जाने पर इन वेरिएबल की वैल्यू बदल देता है।
अनाम फंक्शन को N बार कॉल करने पर फिबोनाची अनुक्रम की Nवीं संख्या मिलती है, जो 0 से शुरू होती है। अंदर वाला अनाम फंक्शन अपने बाहरी फंक्शन fib के लोकल वेरिएबल (n1 और n2) तक पहुँच सकता है।
फंक्शन वैल्यू को कॉल के बीच अपनी स्थिति बनाए रखने का यह बहुत अच्छा तरीका है।
हम कहते हैं कि यह अनाम फंक्शन वेरिएबल n1 और n2 का क्लोज़र है। क्लोज़र का इस्तेमाल प्रोग्रामिंग में बहुत जगह होता है, और हो सकता है आप दूसरी भाषाओं में भी इनका समर्थन देखें।
Bob एक वित्तीय सलाहकार है और लोगों को उनका खर्च संभालने में मदद करता है। Bob के ग्राहक उसे जाँचने के लिए खर्च के रिकॉर्ड भेजते हैं। Bob के पास पिछली अवधियों के रिकॉर्ड हैं, ताकि वह खर्च में होने वाले बदलाव देख सके। Bob को कैलेंडर पसंद नहीं हैं और वह तारीखों की जगह Bob epoch इस्तेमाल करता है। Bob epoch उन दिनों की संख्या है जो Bob के ग्राहक के अपना काम शुरू करने के बाद बीते हैं।
इस अभ्यास में आप एक प्रोग्राम बनाएँगे जो Bob को उसके ग्राहकों का खर्च संभालने और उसका विश्लेषण करने में मदद करेगा।
Bob Record और DaysPeriod के साथ काम करता है।
एक Record खर्च के एक रिकॉर्ड को दर्शाता है। इसमें वह दिन शामिल होता है जिस दिन खर्च किया गया, खर्च की गई राशि, और खर्च की श्रेणी।
// Record represents an expense record.
type Record struct {
Day int
Amount float64
Category string
}
एक DaysPeriod दिनों की एक अवधि को दर्शाता है और From दिन से लेकर To दिन तक के सारे दिनों को शामिल करता है।
अवधि में दोनों छोर शामिल होते हैं।
// DaysPeriod represents a period of days.
type DaysPeriod struct {
From int
To int
}
p := DaysPeriod{From: 1, To: 31}
// p represents all days from the day 1 to the day 31:
// - days 1, 20, 16 and 31 are examples of days that are included
// in the range of time specified by p
// - days 50 and 40 are examples of days that are not included
// in the range of time specified by p
Bob को हर दिन बहुत सारे रिकॉर्ड संभालने पड़ते हैं, लेकिन Bob जो विश्लेषण कर रहा है उसके हिसाब से उनमें से सब दिलचस्प नहीं होते। चलिए Bob को रिकॉर्ड छाँटने का कुछ बुनियादी काम करने में मदद करते हैं।
Filter नाम का सामान्य फंक्शन बनाइए, जो किसी फंक्शन द्वारा दिए गए मानदंड के अनुसार रिकॉर्ड छाँटे।
यह फ़िल्टर फंक्शन रिकॉर्ड का एक संग्रह और एक प्रेडिकेट फंक्शन लेता है, और संग्रह में से सिर्फ़ वे रिकॉर्ड लौटाता है जो प्रेडिकेट की शर्त पूरी करते हैं।
records := []Record{
{Day: 1, Amount: 15, Category: "groceries"},
{Day: 11, Amount: 300, Category: "utility-bills"},
{Day: 12, Amount: 28, Category: "groceries"},
}
// Day1Records only returns true for records that are from day 1
func Day1Records(r Record) bool {
return r.Day == 1
}
Filter(records, Day1Records)
// =>
// [
// {Day: 1, Amount: 15, Category: "groceries"}
// ]
Bob को अक्सर ऐसे रिकॉर्ड छाँटने की ज़रूरत पड़ती है जो किसी दी गई अवधि में हों।
ByDaysPeriod फंक्शन बनाइए, जो Bob को ऐसे फ़िल्टर बनाने में मदद करेगा।
यह फंक्शन एक DaysPeriod लेता है और एक फंक्शन लौटाता है, जो एक रिकॉर्ड लेकर बताता है कि वह रिकॉर्ड आर्गुमेंट के रूप में दिए गए DaysPeriod की बताई गई अवधि में है या नहीं।
records := []Record{
{Day: 1, Amount: 15, Category: "groceries"},
{Day: 11, Amount: 300, Category: "utility-bills"},
{Day: 12, Amount: 28, Category: "groceries"},
{Day: 26, Amount: 300, Category: "university"},
{Day: 28, Amount: 1300, Category: "rent"},
}
period := DaysPeriod{From: 1, To: 15}
Filter(records, ByDaysPeriod(period))
// =>
// [
// {Day: 1, Amount: 15, Category: "groceries"},
// {Day: 11, Amount: 300, Category: "utility-bills"},
// {Day: 12, Amount: 28, Category: "groceries"},
// ]
समय की अवधि के अलावा, Bob को रिकॉर्ड उनकी श्रेणी के अनुसार भी छाँटने की ज़रूरत पड़ती है।
ByCategory फंक्शन बनाइए, जो Bob को ऐसे फ़िल्टर बनाने में मदद करेगा।
यह फंक्शन एक श्रेणी लेता है और एक फंक्शन लौटाता है, जो एक रिकॉर्ड लेकर बताता है कि उस रिकॉर्ड की श्रेणी आर्गुमेंट के रूप में दी गई श्रेणी जैसी ही है या नहीं।
records := []Record{
{Day: 1, Amount: 15, Category: "groceries"},
{Day: 11, Amount: 300, Category: "utility-bills"},
{Day: 12, Amount: 28, Category: "groceries"},
{Day: 28, Amount: 1300, Category: "rent"},
}
Filter(records, ByCategory("groceries"))
// =>
// [
// {Day: 1, Amount: 15, Category: "groceries"},
// {Day: 12, Amount: 28, Category: "groceries"},
// ]
Bob यह भी जानना चाहता है कि किसी अवधि में कुल कितना खर्च हुआ।
TotalByPeriod फंक्शन बनाइए, जो दिनों की अवधि में खर्चों का जोड़ लौटाए।
records := []Record{
{Day: 15, Amount: 16, Category: "entertainment"},
{Day: 32, Amount: 20, Category: "groceries"},
{Day: 40, Amount: 30, Category: "entertainment"}
}
p1 := DaysPeriod{From: 1, To: 30}
p2 := DaysPeriod{From: 31, To: 60}
TotalByPeriod(records, p1)
// => 16
TotalByPeriod(records, p2)
// => 50
अपने ग्राहकों के लिए बनाई जाने वाली सबसे जटिल रिपोर्टों के लिए Bob को एक ही समय पर श्रेणी और समय की अवधि, दोनों के अनुसार रिकॉर्ड छाँटने की ज़रूरत पड़ती है। यानी Bob यह जानना चाहता है कि किसी दी गई अवधि में एक श्रेणी के रिकॉर्ड पर कुल कितना खर्च हुआ।
CategoryExpenses फंक्शन बनाइए, जो दिनों की एक दी गई अवधि में किसी एक श्रेणी में हुए खर्च की कुल राशि लौटाता है।
यह फंक्शन इन दो स्थितियों में फ़र्क भी करना चाहिए: जब दी गई श्रेणी खर्च के रिकॉर्ड में ही मौजूद न हो, और जब दी गई अवधि में उस श्रेणी का कोई खर्च न हो।
जब वह श्रेणी किसी भी रिकॉर्ड की श्रेणी न हो (समय की अवधि चाहे कुछ भी हो), तो फंक्शन को एक एरर लौटानी चाहिए।
p1 := DaysPeriod{From: 1, To: 30}
p2 := DaysPeriod{From: 31, To: 60}
records := []Record{
{Day: 1, Amount: 15, Category: "groceries"},
{Day: 11, Amount: 300, Category: "utility-bills"},
{Day: 12, Amount: 28, Category: "groceries"},
{Day: 26, Amount: 300, Category: "university"},
{Day: 28, Amount: 1300, Category: "rent"},
}
CategoryExpenses(records, p1, "entertainment")
// => 0, error(unknown category entertainment)
CategoryExpenses(records, p1, "rent")
// => 1300, nil
CategoryExpenses(records, p2, "rent")
// => 0, nil
Exercism पर साइन अप कीजिए और Go को 34 कॉन्सेप्ट165 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।