GoCasts آموزش Go به زبان ساده

بیش از ۱۰۰۰ شرکت‌کننده یادگیری Go و Backend رو از امروز شروع کن
ثبت‌نام دوره + تیم‌سازی

آموزش توابع در گولنگ - راهنمای کامل Function در Go

توابع یکی از مهم‌ترین بخش‌های هر زبان برنامه‌نویسی هستند. در Go، توابع شهروندان درجه یک (first-class citizens) هستند، یعنی می‌توانید آنها را به متغیرها assign کنید، به توابع دیگر پاس دهید و از توابع برگردانید.

تعریف تابع

سینتکس پایه:

func functionName(parameters) returnType {
    // بدنه تابع
    return value
}

مثال ساده

package main

import "fmt"

// تابع بدون پارامتر و بازگشت
func sayHello() {
    fmt.Println("سلام!")
}

// تابع با پارامتر
func greet(name string) {
    fmt.Printf("سلام %s!\n", name)
}

// تابع با پارامتر و بازگشت
func add(a int, b int) int {
    return a + b
}

// پارامترهای هم‌نوع - سینتکس کوتاه
func multiply(a, b int) int {
    return a * b
}

func main() {
    sayHello()
    greet("علی")
    result := add(5, 3)
    fmt.Println("نتیجه:", result)
}

چندین مقدار بازگشتی

Go از چندین مقدار بازگشتی پشتیبانی می‌کند:

package main

import (
    "errors"
    "fmt"
)

// دو مقدار بازگشتی
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("تقسیم بر صفر امکان‌پذیر نیست")
    }
    return a / b, nil
}

// مقادیر بازگشتی نام‌گذاری شده
func getStats(numbers []int) (min, max, sum int) {
    if len(numbers) == 0 {
        return 0, 0, 0
    }

    min = numbers[0]
    max = numbers[0]
    sum = 0

    for _, n := range numbers {
        if n < min {
            min = n
        }
        if n > max {
            max = n
        }
        sum += n
    }

    return  // بازگشت ضمنی
}

func main() {
    // مدیریت خطا
    result, err := divide(10, 2)
    if err != nil {
        fmt.Println("خطا:", err)
    } else {
        fmt.Println("نتیجه:", result)
    }

    // نادیده گرفتن یک مقدار با _
    _, err = divide(10, 0)
    if err != nil {
        fmt.Println("خطا:", err)
    }

    // استفاده از getStats
    numbers := []int{5, 2, 8, 1, 9}
    min, max, sum := getStats(numbers)
    fmt.Printf("min=%d, max=%d, sum=%d\n", min, max, sum)
}

Variadic Functions (توابع با پارامتر متغیر)

package main

import "fmt"

// تابع با تعداد پارامتر متغیر
func sum(numbers ...int) int {
    total := 0
    for _, n := range numbers {
        total += n
    }
    return total
}

// ترکیب پارامتر عادی و variadic
func printf(format string, args ...interface{}) {
    fmt.Printf(format, args...)
}

func main() {
    // فراخوانی با تعداد مختلف آرگومان
    fmt.Println(sum())           // 0
    fmt.Println(sum(1))          // 1
    fmt.Println(sum(1, 2, 3))    // 6
    fmt.Println(sum(1, 2, 3, 4, 5))  // 15

    // پاس دادن slice با ...
    nums := []int{1, 2, 3, 4, 5}
    fmt.Println(sum(nums...))    // 15
}

توابع ناشناس (Anonymous Functions)

package main

import "fmt"

func main() {
    // تعریف و فراخوانی فوری
    func() {
        fmt.Println("تابع ناشناس!")
    }()

    // تعریف و assign به متغیر
    greet := func(name string) {
        fmt.Printf("سلام %s!\n", name)
    }
    greet("علی")

    // تابع ناشناس با بازگشت
    add := func(a, b int) int {
        return a + b
    }
    fmt.Println(add(3, 4))

    // پاس دادن تابع به تابع دیگر
    numbers := []int{1, 2, 3, 4, 5}
    doubled := transform(numbers, func(n int) int {
        return n * 2
    })
    fmt.Println(doubled)  // [2 4 6 8 10]
}

func transform(nums []int, fn func(int) int) []int {
    result := make([]int, len(nums))
    for i, n := range nums {
        result[i] = fn(n)
    }
    return result
}

Closure

Closure تابعی است که به متغیرهای خارج از بدنه خود دسترسی دارد:

package main

import "fmt"

// تابعی که تابع برمی‌گرداند
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

// تولیدکننده شناسه
func idGenerator(prefix string) func() string {
    id := 0
    return func() string {
        id++
        return fmt.Sprintf("%s-%d", prefix, id)
    }
}

func main() {
    // هر counter مستقل است
    c1 := counter()
    c2 := counter()

    fmt.Println(c1())  // 1
    fmt.Println(c1())  // 2
    fmt.Println(c1())  // 3
    fmt.Println(c2())  // 1 (مستقل از c1)

    // تولید شناسه
    userID := idGenerator("user")
    orderID := idGenerator("order")

    fmt.Println(userID())   // user-1
    fmt.Println(userID())   // user-2
    fmt.Println(orderID())  // order-1
}

defer

defer اجرای تابع را به پایان تابع جاری موکول می‌کند:

package main

import (
    "fmt"
    "os"
)

func main() {
    // ترتیب اجرا: LIFO (آخرین، اول)
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
    fmt.Println("شروع")
    // خروجی: شروع، 3، 2، 1

    // کاربرد عملی: بستن فایل
    readFile()
}

func readFile() {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("خطا:", err)
        return
    }
    defer file.Close()  // تضمین بسته شدن فایل

    // خواندن و پردازش فایل...
    fmt.Println("فایل باز شد")
}

// defer با مقادیر
func deferWithValues() {
    x := 10
    defer fmt.Println("x =", x)  // x = 10 (مقدار در زمان defer)
    x = 20
    fmt.Println("x در پایان:", x)  // x در پایان: 20
}

// defer برای زمان‌سنجی
func measureTime() func() {
    start := time.Now()
    return func() {
        fmt.Printf("زمان اجرا: %v\n", time.Since(start))
    }
}

func slowFunction() {
    defer measureTime()()

    // کار زمان‌بر
    time.Sleep(2 * time.Second)
}

متدها (Methods)

متد تابعی است که به یک type متصل شده:

package main

import (
    "fmt"
    "math"
)

// تعریف struct
type Rectangle struct {
    Width  float64
    Height float64
}

type Circle struct {
    Radius float64
}

// متد با value receiver
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// متد با pointer receiver (می‌تواند struct را تغییر دهد)
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

// متد برای Circle
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}

    fmt.Printf("مساحت: %.2f\n", rect.Area())
    fmt.Printf("محیط: %.2f\n", rect.Perimeter())

    // تغییر با pointer receiver
    rect.Scale(2)
    fmt.Printf("مساحت جدید: %.2f\n", rect.Area())

    circle := Circle{Radius: 5}
    fmt.Printf("مساحت دایره: %.2f\n", circle.Area())
}

Interface

package main

import (
    "fmt"
    "math"
)

// تعریف interface
type Shape interface {
    Area() float64
    Perimeter() float64
}

type Rectangle struct {
    Width, Height float64
}

type Circle struct {
    Radius float64
}

// پیاده‌سازی interface برای Rectangle
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// پیاده‌سازی interface برای Circle
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

// تابع که از interface استفاده می‌کند
func printShapeInfo(s Shape) {
    fmt.Printf("مساحت: %.2f\n", s.Area())
    fmt.Printf("محیط: %.2f\n", s.Perimeter())
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    circle := Circle{Radius: 7}

    printShapeInfo(rect)
    fmt.Println("---")
    printShapeInfo(circle)

    // slice از interface
    shapes := []Shape{rect, circle}
    for _, shape := range shapes {
        fmt.Printf("مساحت: %.2f\n", shape.Area())
    }
}

Higher-Order Functions

توابعی که تابع می‌گیرند یا تابع برمی‌گردانند:

package main

import "fmt"

// Map
func Map[T, U any](slice []T, fn func(T) U) []U {
    result := make([]U, len(slice))
    for i, v := range slice {
        result[i] = fn(v)
    }
    return result
}

// Filter
func Filter[T any](slice []T, fn func(T) bool) []T {
    var result []T
    for _, v := range slice {
        if fn(v) {
            result = append(result, v)
        }
    }
    return result
}

// Reduce
func Reduce[T, U any](slice []T, initial U, fn func(U, T) U) U {
    result := initial
    for _, v := range slice {
        result = fn(result, v)
    }
    return result
}

func main() {
    numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    // دوبرابر کردن
    doubled := Map(numbers, func(n int) int {
        return n * 2
    })
    fmt.Println("Doubled:", doubled)

    // فیلتر زوج‌ها
    evens := Filter(numbers, func(n int) bool {
        return n%2 == 0
    })
    fmt.Println("Evens:", evens)

    // مجموع
    sum := Reduce(numbers, 0, func(acc, n int) int {
        return acc + n
    })
    fmt.Println("Sum:", sum)
}

جدول خلاصه

مفهوم سینتکس کاربرد
تابع ساده func name() {} عملیات پایه
با پارامتر func name(a int) {} ورودی
با بازگشت func name() int {} خروجی
چند بازگشت func name() (int, error) {} خروجی + خطا
Variadic func name(args ...int) {} تعداد متغیر
ناشناس func() {}() inline
Closure func() func() {} دسترسی به scope بیرونی
defer defer fn() اجرای تأخیری
Method func (r T) name() {} متصل به type

قدم‌های بعدی

منابع

بیش از ۱۰۰۰ شرکت‌کننده یادگیری Go و Backend رو از امروز شروع کن
ثبت‌نام دوره + تیم‌سازی