1.7.1 تابع چیست؟ #
در برنامهنویسی، تابع به تکه کدهایی گفته میشود که برای انجام عملی خاص استفاده میشوند. به کمک توابع میتوانیم تکه کدها را از یکدیگر جدا کنیم و از تکرار کدها جلوگیری کنیم.
همانند شکل زیر تابع مانند یک جعبه (یا قطعه کد) است. که یک مقدار ورودی گرفته و روی آن پردازشهایی انجام میدهد و یک خروجی تولید میکند. تابع میتواند هر چندتا ورودی یا خروجی داشته باشد.
1.7.1.1 چرا از توابع در برنامه نویسی استفاده میکنیم؟ #
- با استفاده از توابع، میتوانیم یک قطعه کد را یک بار بنویسیم و چندین بار از آن استفاده کنیم.
- با استفاده از توابع، حجم کدنویسی ما کمتر میشود و سرعت توسعه نرمافزار بیشتر میشود.
- توابع به ما کمک میکنند تا کد خود را به تکههایی کوچکتر تقسیم کنیم تا برنامه ی ما خوانا و قابل درکتر باشد. همچنین نگهداری و اشکال زدایی کد را آسانتر میکند.
1.7.2 توابع در زبان گولنگ #
توابع یکی از قابلیتهای مهم هر زبانی محسوب میشوند. توابع در گولنگ داری سینتکس سادهای هستند.
1.7.2.1 تعریف یک تابع #
- func - با این کلید واژه یک تابع تعریف میکنیم.
- function_name - نام تابع باید یکتا و در طول برنامه منحصر به فرد باشد.
- Parameter-list - پارامترهای ورودی در این قسمت تعریف میشوند. پارامترها اختیاری هستند یعنی ممکن است یک تابع هیچ پارامتری نداشته باشد.
- Return_type - نوع دادههای بازگشتی را در این قسمت مشخص میکنیم و در برخی توابع عملیات موردنظر را بدون بازگرداندن مقداری انجام میدهند در این توابع نیازی به تعیین return_type نیست.
- Function Body - این قسمت شامل کدهایی است که نشان میدهد تابع چه کارهایی انجام میدهد.
1.7.2.1.1 مثال #
1package main
2
3import "fmt"
4
5func plus(a int, b int) int {
6 return a + b
7}
8
9func main() {
10 fmt.Println(plus(4, 13))
11}
- در اینجا یک تابع با نام plus تعریف کردیم که دو پارامتر a, b را با نوع داده int دریافت میکند و جمع این دو عدد را حساب میکند. توجه کنید که همیشه باید نوع داده را مشخص کنیم.
- بعد از تعریف پارامترهای ورودی و قبل از قرار دادن کد داخل براکت int را میبینید که نوع خروجی داده را مشخص میکند، یعنی خروجی این تابع باید از نوع int باشد.
- برای صدا زدن توابع کافی است نام تابع رو همراه با پرانتز باز و بسته تایپ کنید, برای مثال ()plus.
در صورتی که پارامترهای ورودی از یک نوع باشند بجای تعریف نوع بهصورت تکی، میتوانیم نوع داده را در آخر همه پارامترهای هم نوع مشخص کنیم.
1.7.2.1.2 الگو دیگر تعریف تابع: #
1package main
2
3import "fmt"
4
5func main() {
6 plus := func (a int, b int) int {
7 return a + b
8 }
9 fmt.Println(plus(3, 4))
10}
1.7.3 قوانین نام گذاری تابع در گولنگ #
- نام تابع باید با یک حرف شروع شود.
- نام تابع فقط میتواند شامل حروف-عدد و underscores باشد. (
A-z
,0-9
,_
) - نام تابع به حروف کوچک و بزرگ حساس است.
- در نامگذاری تابع از فاصله نمیتوانیم استفاده کنیم.
1.7.4 توابع چند بازگشتی در گولنگ (Multiple results) #
همچنین در گولنگ توابع میتوانند چندین مقادیر را برگردانند.
1package main
2
3import "fmt"
4
5func vals() (int, int) {
6 return 3, 7
7}
8
9func main() {
10
11 a, b := vals()
12 fmt.Println(a)
13 fmt.Println(b)
14
15 _, c := vals()
16 fmt.Println(c)
17}
- در کد بالا تابعی ساختیم با اسم vals که دو خروجی از نوع int دارد بنابراین نوع تعریف توابع چندبازگشتی متفاوت است و بصورت (data-type, data-type, … ) است.
- در مثال اول ما با کمک دو متغیر a,b دو خروجی تابع vals را دریافت کردیم.
- در مثال دوم ما با استفاده از _ blank identifier از دریافت یا استخراج خروجی اول صرف نظر کردیم و فقط خروجی دوم با متغیر c را دریافت کردیم.
در توابع چند بازگشتی خروجی آنها را باید با , از هم جدا کرد
1.7.4.1 مقادیر بازگشتی نام گذاری شده (Named Return Values) #
در گولنگ میتوانیم مقدار بازگشتی یک تابع را نامگذاری کنیم. به مثال زیر توجه کنید:
1package main
2
3import "fmt"
4
5func split(sum int) (x, y int) {
6 x = sum * 4 / 9
7 y = sum - x
8 return
9}
10
11func main() {
12 fmt.Println(split(17))
13}
- ما در این تابع مقدار بازگشتی را x, y از نوع int نام گذاری میکنیم.
- در اینجا یک دستور return بدون تعیین آرگومان بازگشتی، مقادیر نام گذاری شده را باز میگرداند، که به عنوان Naked return شناخته میشود.
- از naked return باید در توابع کوتاه مانند مثال بالا استفاده شود. استفاده آن در تابع طولانی منجر به کاهش خوانایی کد می شود.
1.7.5 توابع متنوع در گولنگ (Variadic Functions) #
یکی از قابلیتهای گو وجود توابع متنوع است. منظور از توابع متنوع توابعی هستند که بدون محدودیت پارامتر دریافت میکنند (این نکته رو در نظر بگیرین که نباید تایپ ورودیها با یکدیگر فرق کند، برای مثال همه باید int باشند)
.
1package main
2
3import "fmt"
4
5func sum(nums ...int) {
6 fmt.Print(nums, " ")
7 total := 0
8
9 for _, num := range nums {
10 total += num
11 }
12 fmt.Println(total)
13}
14
15func main() {
16
17 sum(1, 2)
18 sum(1, 2, 3)
19
20 nums := []int{1, 2, 3, 4}
21 sum(nums...)
22}
- در اینجا تابعی تعریف کردیم که تعداد دلخواهی از آرگومانها از نوع int را به کمک …
(بهش میگن Ellipsis)
که قبل از نوع داده قرار گرفته به داخل تابع منتقل میکند. - برای صدا زدن این توابع میتوان به روش sum(num1, num2, …) عمل کرد.
- اگر شما دادهای با نوع slice دارید میتوانید آن را به کمک اپراتور …
(Ellipsis)
به صورت sum(nums…) به داخل تابع انتقال بدید.
1.7.6 توابع ناشناس در گولنگ (Anonymous Functions) #
در زبان گولنگ میتوانیم تابع بدون نام تعریف کنیم، که به عنوان توابع ناشناس شناخته میشوند.
1package main
2
3import "fmt"
4
5func main() {
6
7 // anonymous function
8 var sum = func(n1, n2 int) int {
9 sum := n1 + n2
10
11 return sum
12 }
13
14 // function call
15 result := sum(5, 3)
16
17 fmt.Println("Sum is:", result)
18
19}
- از آنجایی که توابع ناشناس نامی ندارد ما در بعضی اوقات آنها را به یک متغیر اختصاص میدهیم سپس از نام متغیر برای فراخوانی تابع استفاده میکنیم. در این مثال میبینید که ما از sum برای فراخوانی تابع استفاده میکنیم.
- مانند توابع معمولی ما میتوانیم برای تابع ناشناس پارامتر تعریف کنیم و همچنین مقداری را از تابع برگردانیم در این مثال تابع دو مقدار با نوع داده int دریافت میکنید و یک خروجی با نوع int دارد.
- تابع ناشناس را میتوان برای عملکردهایی که نیازی به نامگذاری ندارند و معمولا برای استفاده کوتاه مدت هست، استفاده کرد.
اما یکی از موارد کاربردی توابع ناشناس در گولنگ پاس دادن آنها به توابعی هستند که تابعی را تحت عنوان پارامتر ورودی دریافت میکنند. در مثال زیر ما یک تابع تعریف کردیم که در پارامتر سوم یک تابع دریافت میکند که باید دو ورودی int و یک خروجی int داشته باشد.
1package main
2
3import "fmt"
4
5func add10AndSum(num1 int, num2 int, sum func(n1, n2 int) int) {
6 result := sum(num1+10, num2+10)
7 fmt.Println("Sum by adding 10 is:", result)
8}
9
10func main() {
11 add10AndSum(5, 3, func(n1, n2 int) int {
12 sum := n1 + n2
13
14 return sum
15 })
16}
1.7.7 توابع از پیش تعریف شده (Built-in Function) #
در گولنگ علاوه بر تابعهایی که توسط کاربر تعریف میشود یکسری توابع وجود دارد که از قبل تعریف شدهاند که طراحان این زبان برای سهولت کار برنامهنویسان این توابع را نوشتهاند و آن را همراه زبان گولنگ ارائه میدهند.
https://book.gofarsi.ir/chapter-1/go-builtins/
1.7.7 کلوژر(Function closure) #
یک نوع دیگری از anonymous function ها در زبان گولنگ، کلوژر ها هستند. به بیان ساده زمانی که یک فانکشن درون خودش، متغیر هایی که اسکوپ آنها خارج از اسکوپ خودش قرار دارد استفاده کند. کلوژر میگوییم.
1package main
2
3import "fmt"
4
5func main() {
6 number := 1
7
8 func() {
9 fmt.Println(number * 2)
10 }()
11}
در مثال بالا ما یک anonymous function داریم که درون خودش از متغیر اسکوپی که خارج از خودش قرار دارد استفاده کرده است. به این عمل کلوژر می گوییم.
زمانی که از کلوژر ها استفاده می کنید. برخی مواقع بهتر است بهجای اینکه بهصورت مستقیم به متغیر اسکوپ خارجی دسترسی داشته باشید، در پارامتر های ورودی مقدار را دریافت کنید. به مثال زیر دقت کنید(در این مثال از concurrency استفاده کردیم. اگر آشنایی ندارید با مطالعه مقدمه ای از فصل سوم مثال را متوجه می شوید)
1package main
2
3import (
4 "fmt"
5 "time"
6)
7
8func main() {
9 for i := 0; i < 10; i++ {
10 go func() {
11 fmt.Println(i)
12 }()
13 }
14
15 time.Sleep(time.Second * 1)
16}
خروجی کد برخلاف چیزی که تصور می کنیم به این شکل است:
این اتفاق به این دلیل می افتد چون کلوژر ها بهصورت مستقیم به مقدار اسکوپ بالایی خود دسترسی دارند. قبل از اینکه گوروتین ها مقدار را چاپ کنند حلقه به انتها می رسد و مقدار i برابر با 10 می شود. برای همین در خروجی همه گوروتین ها مقدار 10 چاپ می شود. برای حل این مشکل مقدار i را در پارامتر فانکشن دریافت می کنیم:
1package main
2
3import (
4 "fmt"
5 "time"
6)
7
8func main() {
9 for i := 0; i < 10; i++ {
10 go func(num int) {
11 fmt.Println(num)
12 }(i)
13 }
14
15 time.Sleep(time.Second * 1)
16}
پس زمانی که از کلوژر ها استفاده می کنید به این نکات دقت کنید.