1 Khái niệm cơ bản về hàm
Trong lập trình, một hàm là một đoạn mã thực hiện một nhiệm vụ cụ thể và có thể có các tham số đầu vào và giá trị trả về. Trong ngôn ngữ Go, hàm được sử dụng rộng rãi để tổ chức và tái sử dụng mã. Sử dụng hàm một cách hiệu quả có thể làm cho mã nguồn ngắn gọn hơn, dễ đọc và dễ bảo trì.
Hàm là một thành phần cốt lõi của ngôn ngữ Go, và việc hiểu cách khai báo và định nghĩa hàm là rất quan trọng để viết mã nguồn hiệu quả và dễ đọc.
2 Định nghĩa hàm
2.1 Khai báo hàm
Trong ngôn ngữ Go, dạng chung của việc khai báo hàm là:
func tênHàm(thamSố) kiểuTrảVề {
// Nội dung của hàm
}
Hãy phân tích các phần này:
- Từ khóa
func
được sử dụng để khai báo một hàm. -
tênHàm
là tên của hàm, tuân theo quy ước đặt tên trong ngôn ngữ Go. Một hàm có chữ cái đầu viết hoa có thể xuất, nghĩa là nó có thể nhìn thấy bên ngoài gói; một hàm có chữ cái đầu viết thường không thể xuất và chỉ có thể sử dụng trong cùng một gói. -
thamSố
là danh sách các tham số mà hàm nhận được, được phân tách bằng dấu phẩy. Cần phải xác định loại của mỗi tham số và nếu có nhiều tham số cùng loại, thì chỉ cần xác định loại một lần. -
kiểuTrảVề
là kiểu dữ liệu của giá trị trả về của hàm. Nếu hàm không trả về giá trị, phần này có thể bị bỏ qua. Nếu hàm trả về nhiều giá trị, chúng cần phải được bao quanh trong dấu ngoặc.
Ví dụ, chúng ta có thể khai báo một hàm đơn giản để tính tổng của hai số nguyên:
func Tổng(a int, b int) int {
return a + b
}
Trong ví dụ này, tên hàm là Tổng
, nó nhận hai tham số kiểu int
(a và b), và trả về tổng của chúng với kiểu trả về là int
.
2.2 Danh sách Tham số
Danh sách tham số xác định các tham số mà hàm chấp nhận và loại của từng tham số. Tham số là một loại biến đặc biệt được sử dụng để truyền dữ liệu vào một hàm.
func Chào(name string, age int) {
fmt.Printf("Xin chào, %s! Bạn đã %d tuổi.\n", name, age)
}
Trong hàm Chào
này, name
và age
là các tham số. Kiểu của name
là string
, và kiểu của age
là int
. Khi gọi hàm này, các giá trị thực tế phải được cung cấp cho các tham số này.
2.3 Kiểu Trả Về
Hàm có thể trả về kết quả tính toán, và kiểu trả về xác định kiểu dữ liệu của giá trị trả về của hàm. Hàm có thể không có giá trị trả về, hoặc có thể có một hoặc nhiều giá trị trả về.
Nếu một hàm có nhiều giá trị trả về, kiểu của chúng cần được bao quanh trong dấu ngoặc khi khai báo:
func Chia(dividend, divisor float64) (float64, error) {
if divisor == 0 {
return 0, errors.New("không thể chia cho số không")
}
return dividend / divisor, nil
}
Hàm Chia
ở đây trả về hai giá trị: thương và một thông báo lỗi. Nếu mẫu số bằng không, một lỗi được trả về.
2.4 Tham số đa dạng
Trong Golang, khi chúng ta không chắc chắn về số lượng tham số mà người gọi sẽ truyền khi định nghĩa một hàm, chúng ta có thể sử dụng các tham số đa dạng. Tham số đa dạng được chỉ định bằng dấu ba chấm ...
, có nghĩa là hàm có thể chấp nhận bất kỳ số lượng tham số nào. Điều này rất hữu ích khi xử lý một lượng dữ liệu không chắc chắn hoặc triển khai các loại hàm cụ thể như định dạng, tóm lược hoặc chức năng lặp.
Các Tình huống Ứng dụng
Tham số biến có thể được sử dụng phổ biến trong những tình huống sau:
-
Ghép chuỗi: như các hàm
fmt.Sprintf
vàstrings.Join
. - Xử lý mảng/dãy: khi xử lý mảng hoặc dãy có độ dài biến đổi, như tính tổng hoặc ghép nối nhiều dãy.
-
Ghi log và xử lý lỗi: khi xử lý nhiều lỗi hoặc ghi lại nhiều thông tin log, như
log.Printf
hoặc các hàm tóm tắt lỗi tùy chỉnh. - Các hàm trợ giúp: được sử dụng trong các API hoặc thư viện để cung cấp cách gọi hàm linh hoạt hơn cho người dùng.
Sử dụng Tham số Biến
Dưới đây là một ví dụ đơn giản về việc sử dụng tham số biến:
// Định nghĩa một hàm có thể nhận một số nguyên tham số biến và trả về tổng của chúng
func Sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
func main() {
// Bạn có thể truyền bất kỳ số lượng tham số nào
fmt.Println(Sum(1, 2)) // Kết quả: 3
fmt.Println(Sum(1, 2, 3, 4)) // Kết quả: 10
// Bạn cũng có thể truyền một slice và sử dụng dấu ba chấm sau slice đó
numbers := []int{1, 2, 3, 4, 5}
fmt.Println(Sum(numbers...)) // Kết quả: 15
}
Trong hàm Sum
, nums
là một slice các số nguyên chứa tất cả các tham số được truyền vào hàm Sum
. Chúng ta có thể sử dụng vòng lặp range
để lặp qua slice này và tính tổng.
Đáng chú ý là tham số biến bên trong hàm thực tế là một slice, vì vậy tất cả các hoạt động liên quan đến slice có thể được sử dụng. Khi bạn cần truyền một slice làm tham số biến cho một hàm, chỉ cần thêm dấu ba chấm ...
sau slice đó.
Việc sử dụng tham số biến có thể làm cho việc gọi hàm linh hoạt và ngắn gọn hơn, nhưng cũng sẽ ảnh hưởng một chút đến hiệu suất vì việc sử dụng tham số biến liên quan đến việc tạo slice và phân bổ bộ nhớ. Do đó, cần cẩn trọng khi đặt yêu cầu về hiệu suất nghiêm ngặt.
Mẹo: Giải thích chi tiết về slice sẽ được cung cấp trong các chương sau. Nếu bạn có kinh nghiệm với ngôn ngữ lập trình khác, bạn có thể tạm thời coi chúng như các mảng.
3 Gọi Hàm
3.1 Gọi Hàm Cơ Bản
Gọi một hàm có nghĩa là thực thi mã của hàm. Trong Go, việc gọi một hàm đã được định nghĩa rất đơn giản, chỉ cần sử dụng tên hàm và truyền các tham số phù hợp. Ví dụ:
result := add(3, 4)
fmt.Println(result) // Kết quả: 7
Trong ví dụ này, hàm add
được gọi và hai số nguyên được truyền làm tham số, sau đó kết quả trả về được gán cho biến result
.
3.2 Truyền Tham số
Khi truyền tham số cho một hàm, Go mặc định sử dụng pass-by-value, điều này có nghĩa là truyền một bản sao của giá trị tham số và dữ liệu gốc sẽ không bị thay đổi. Tuy nhiên, nếu bạn muốn hàm sửa đổi các biến bên ngoài hoặc vì lý do hiệu suất, bạn có thể sử dụng pass-by-reference, chẳng hạn như sử dụng con trỏ hoặc truyền các cấu trúc lớn.
Dưới đây là các ví dụ về pass-by-value và pass-by-reference:
// Ví dụ về pass-by-value
func double(val int) {
val *= 2
}
// Ví dụ về pass-by-reference
func doublePtr(val *int) {
*val *= 2
}
func main() {
value := 3
double(value)
fmt.Println(value) // Xuất ra 3, giá trị không thay đổi
doublePtr(&value)
fmt.Println(value) // Xuất ra 6, giá trị tăng gấp đôi
}
Trong ví dụ trên, hàm double
cố gắng làm gấp đôi giá trị val
được truyền, nhưng nó chỉ gấp đôi bản sao của nó, để lại biến value
gốc không thay đổi. Tuy nhiên, hàm doublePtr
thay đổi giá trị của biến gốc bằng cách nhận một con trỏ đến biến số nguyên làm tham số.