1. Khái niệm cơ bản về func

Trong Golang, func được dùng để khai báo một hàm. Hàm là một tập hợp các câu lệnh được gom nhóm lại để thực thi một nhiệm vụ cụ thể.


1.1 Khai báo và sử dụng hàm

Cú pháp cơ bản:

func functionName(params) (return type) {
    // Logic của hàm
    return value
}

Ví dụ:

package main

import "fmt"

func greet() {
    fmt.Println("Xin chào, Golang!")
}

func main() {
    greet() // Gọi hàm
}

Giải thích:

  • func greet(): Khai báo hàm greet không nhận tham số và không trả về giá trị.
  • fmt.Println: In ra màn hình.

1.2 Hàm với tham số

Hàm có thể nhận tham số đầu vào để xử lý.

Ví dụ:

func greet(name string) {
    fmt.Printf("Xin chào, %s!\n", name)
}

func main() {
    greet("An") // Gọi hàm với tham số
}

Giải thích:

  • name string: Tham số name có kiểu dữ liệu string.
  • %s: Placeholder để chèn giá trị của chuỗi.

1.3 Hàm với giá trị trả về

Hàm có thể trả về kết quả sau khi thực thi.

Ví dụ:

func add(a int, b int) int {
    return a + b
}

func main() {
    result := add(3, 5)
    fmt.Println("Kết quả:", result)
}

Giải thích:

  • int: Kiểu dữ liệu trả về của hàm.
  • return: Trả về giá trị cho hàm.

2. Các tính năng nâng cao của func


2.1 Giá trị trả về nhiều

Golang hỗ trợ trả về nhiều giá trị từ một hàm.

Ví dụ:

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, fmt.Errorf("không thể chia cho 0")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 2)
    if err != nil {
        fmt.Println("Lỗi:", err)
    } else {
        fmt.Println("Kết quả:", result)
    }
}

Giải thích:

  • Hàm divide trả về hai giá trị:
  • Kết quả chia.
  • Lỗi nếu b == 0.

2.2 Tham số mặc định (biến động số lượng tham số)

Golang không hỗ trợ trực tiếp tham số mặc định, nhưng có thể dùng tham số biến động.

Ví dụ:

func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

func main() {
    fmt.Println("Tổng:", sum(1, 2, 3, 4, 5))
}

Giải thích:

  • ...int: Cho phép truyền một số lượng tham số không cố định.
  • range numbers: Duyệt qua các tham số.

2.3 Hàm ẩn danh (Anonymous Function)

Hàm có thể được khai báo mà không cần tên.

Ví dụ:

func main() {
    greet := func(name string) {
        fmt.Printf("Chào %s\n", name)
    }
    greet("Huy")
}

Giải thích:

  • func(name string): Khai báo hàm ẩn danh.
  • Gán hàm ẩn danh cho biến greet.

2.4 Hàm như tham số

Hàm có thể được truyền như một tham số.

Ví dụ:

func operate(a, b int, op func(int, int) int) int {
    return op(a, b)
}

func add(a, b int) int {
    return a + b
}

func main() {
    result := operate(3, 5, add)
    fmt.Println("Kết quả:", result)
}

Giải thích:

  • op func(int, int) int: Hàm op là tham số.
  • Hàm add được truyền vào operate.

2.5 Hàm đệ quy (Recursive Function)

Hàm gọi lại chính nó để giải quyết bài toán.

Ví dụ:

func factorial(n int) int {
    if n == 0 {
        return 1
    }
    return n * factorial(n-1)
}

func main() {
    fmt.Println("Giai thừa của 5 là:", factorial(5))
}

Giải thích:

  • Hàm factorial gọi lại chính nó để tính giai thừa.

2.6 Hàm method cho struct

Hàm có thể được liên kết với struct.

Ví dụ:

type User struct {
    Name string
}

func (u User) Greet() {
    fmt.Printf("Xin chào, tôi là %s!\n", u.Name)
}

func main() {
    user := User{Name: "An"}
    user.Greet()
}

Giải thích:

  • func (u User): Greet là phương thức của User.
  • u.Name: Truy cập trường Name của struct.

2.7 Trì hoãn thực thi với defer

Lệnh defer sẽ trì hoãn thực thi cho đến khi hàm kết thúc.

Ví dụ:

func main() {
    fmt.Println("Bắt đầu")
    defer fmt.Println("Kết thúc")
    fmt.Println("Đang thực thi")
}

Kết quả:

Bắt đầu
Đang thực thi
Kết thúc

Giải thích:

  • defer: Lệnh fmt.Println("Kết thúc") chỉ được gọi khi hàm kết thúc.

2.8 Hàm Generator

Hàm có thể được sử dụng để tạo các giá trị tuần tự.

Ví dụ:

func generator() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    next := generator()
    fmt.Println(next()) // 1
    fmt.Println(next()) // 2
    fmt.Println(next()) // 3
}

Giải thích:

  • Hàm generator trả về một hàm ẩn danh.
  • Biến count được lưu trữ trong closure của hàm.

3. Tổng kết

  1. Bắt đầu với các khái niệm cơ bản như khai báo, truyền tham số và giá trị trả về.
  2. Tìm hiểu nâng cao với giá trị trả về nhiều, hàm ẩn danh, và defer.
  3. Sử dụng hàm như tham số, đệ quy, và kết hợp với struct để xây dựng chương trình phức tạp.

Với các ví dụ trên, bạn đã sẵn sàng sử dụng func một cách linh hoạt và hiệu quả trong các dự án Go!