Một hàm là một nhóm các câu lệnh cùng nhau thực hiện một công việc. Tất cả chương trình Go đều phải có ít nhất một hàm main()
. Bạn có thể chia code của mình ra làm các hàm nhỏ. Việc chia nhỏ các hàm phụ thuộc hoàn toàn vào bạn, nhưng phải hợp lý, việc chia nhỏ này kiểu như là chia mỗi hàm thực hiện một công việc cụ thể.
Hàm giúp chúng ta chia code ra thành những phần nhỏ hơn để có thể tái sử dụng. Chúng giúp cải thiện khả năng đọc, duy trì, và test cho chương trình.
Khai báo và gọi hàm
Cú pháp phổ biến để khai báo hàm trong Go
func tên_hàm(tên_tham_số kiểu) kiểu trả về { // thân hàm }
Trong Go, chúng ta khai báo một hàm sử dụng từ khoá func
. Một hàm có tên, các tham số đầu vào đi kèm với kiểu được chia tách bởi dấu phẩy, kiểu trả về, và thân hàm.
Ví dụ sau là một hàm đơn giản được gọi là avg
, nhận 2 tham số đầu vào kiểu float64
và trả về trung bình cộng của 2 tham số. Kết quả trả về cũng là float64
func avg(x float64, y float64) float64 { return (x + y) / 2 }
Tiếp theo việc gọi một hàm là tương đối đơn giản, bạn chỉ cần truyền vào các giá trị cho các tham số đầu vào như sau
avg(6.56, 13.44)
Sau đây là toàn bộ ví dụ
package main import "fmt" func avg(x float64, y float64) float64 { return (x + y) / 2 } func main() { x := 5.75 y := 6.25 result := avg(x, y) fmt.Printf("Average of %.2f and %.2f = %.2f\n", x, y, result) }
Kết quả
Average of 5.75 and 6.25 = 6.00
Tham số và kiểu trả về không bắt buộc
Trong hàm, tham số và kiểu trả về là không bắt buộc. Một hàm có thể được khai báo mà không cần tham số đầu vào và kiểu trả về.
Hàm main()
là một hàm như thế.
Một ví dụ khác
func sayHello() { fmt.Println("Hello, World") }
Bạn chỉ cần chỉ định kiểu một lần cho các tham số liên tiếp cùng kiểu
Nếu một hàm có 2 hoặc nhiều tham số liên tiếp mà cùng kiểu, thì nó chỉ cần chỉ định kiểu một lần là đủ, sử dụng kiểu của tham số cuối cùng.
Chúng ta sẽ khai báo lại hàm avg
ở ví dụ trên như sau
func avg(x, y float64) float64 { }
tương tự như chúng ta khai báo
func avg(x float64, y float64) float64 { }
Sau đây là ví dụ khác
func printPersonDetails(firstName, lastName string, age int) { } // giống như - func printPersonDetails(firstName string, lastName string, age int) { }
Hàm với nhiều giá trị trả về
Hàm Go có thể trả về nhiều giá trị. Hầu như tất cả các ngông ngữ đều không hỗ trợ việc này. Nhưng Go thì khác
Giả sử rằng bạn muốn tạo một hàm nhận 2 tham số đầu vào là giá cổ phiếu lúc trước và giá cổ phiếu hiện tại, và trả về số tiền chênh lệch, tỉ lệ phần trăm chênh lệch.
Sau đây là cách thực thi hàm như vậy
func getStockPriceChange(prevPrice, currentPrice float64) (float64, float64) { change := currentPrice - prevPrice percentChange := (change / prevPrice) * 100 return change, percentChange }
Bạn chỉ cẩn chỉ định kiểu trả về được tách với nhau bằng dấu phẩy trong dấu ngoặc đơn, và sau đó trả về các giá trị được tách bằng dấu phẩy từ hàm.
package main import ( "fmt" "math" ) func getStockPriceChange(prevPrice, currentPrice float64) (float64, float64) { change := currentPrice - prevPrice percentChange := (change / prevPrice) * 100 return change, percentChange } func main() { prevStockPrice := 75000.0 currentStockPrice := 100000.0 change, percentChange := getStockPriceChange(prevStockPrice, currentStockPrice) if change < 0 { fmt.Printf("The Stock Price decreased by $%.2f which is %.2f%% of the prev price\n", math.Abs(change), math.Abs(percentChange)) } else { fmt.Printf("The Stock Price increased by $%.2f which is %.2f%% of the prev price\n", change, percentChange) } }
Kết quả
The Stock Price increased by $25000.00 which is 33.33% of the prev price
Trả về lỗi
Nhiều khi giá trị trả về trong Go là một lỗi trong kết quả của một hàm.
Hãy cùng xem hàm sau getStockPriceChange
mà chúng ta đã nhìn thấy ở ví dụ bên trên, trả về vô cực nếu prevPrice
bằng 0. Nếu bạn mong muốn thay thế bằng cách trả về một lỗi, bạn có thể thực hiện bằng cách thêm một kiểu trả về error
, và trả về giá trị lỗi.
func getStockPriceChangeWithError(prevPrice, currentPrice float64) (float64, float64, error) { if prevPrice == 0 { err := errors.New("Previous price cannot be zero") return 0, 0, err } change := currentPrice - prevPrice percentChange := (change / prevPrice) * 100 return change, percentChange, nil }
Kiểu error
được tích hợp trong Go. Chương trình Go sử dụng các giá trị error
để biểu thị trường hợp bất thường xảy ra.
package main import ( "errors" "fmt" "math" ) func getStockPriceChangeWithError(prevPrice, currentPrice float64) (float64, float64, error) { if prevPrice == 0 { err := errors.New("Previous price cannot be zero") return 0, 0, err } change := currentPrice - prevPrice percentChange := (change / prevPrice) * 100 return change, percentChange, nil } func main() { prevStockPrice := 0.0 currentStockPrice := 100000.0 change, percentChange, err := getStockPriceChangeWithError(prevStockPrice, currentStockPrice) if err != nil { fmt.Println("Sorry! There was an error: ", err) } else { if change < 0 { fmt.Printf("The Stock Price decreased by $%.2f which is %.2f%% of the prev price\n", math.Abs(change), math.Abs(percentChange)) } else { fmt.Printf("The Stock Price increased by $%.2f which is %.2f%% of the prev price\n", change, percentChange) } } }
Kết quả
Sorry! There was an error: Previous price cannot be zero
Giá trị trả về được đặt tên
Các giá trị trả về của hàm trong Go có thể được đặt tên. Giá trị trả về được đặt tên như thể bạn định nghĩa chúng ở đầu hàm.
Hãy viết lại hàm getStockPriceChange
với giá trị trả về được đặt tên.
func getNamedStockPriceChange(prevPrice, currentPrice float64) (change, percentChange float64) { change = currentPrice - prevPrice percentChange = (change / prevPrice) * 100 return change, percentChange }
Chúng ta đã thay đổi :=
(khai báo tắt) bằng =
(gán) trong thân hàm. Điều này bởi vì Go định nghĩa tất cả các giá trị trả về được đặt tên và nhờ đó chúng sẵn sàng để sử dụng trong hàm. Vì chúng ta đã định nghĩa, nên chúng ta không thể sử dụng khai báo tắt để định nghĩa lại.
Giá trị trả về được đặt tên cho phép bạn sử dụng kiểu trả về trống, trả về câu lệnh không cần tham số. Khi bạn chỉ định câu lệnh return
không có tham số, nó sẽ trả về giá trị trả về được đặt tên như mặc định.
func getNamedStockPriceChange(prevPrice, currentPrice float64) (change, percentChange float64) { change = currentPrice - prevPrice percentChange = (change / prevPrice) * 100 return }
Hãy chạy thử đoạn code bên trên trong main()
với một vài ví dụ
package main import ( "fmt" "math" ) func getNamedStockPriceChange(prevPrice, currentPrice float64) (change, percentChange float64) { change = currentPrice - prevPrice percentChange = (change / prevPrice) * 100 return } func main() { prevStockPrice := 100000.0 currentStockPrice := 90000.0 change, percentChange := getNamedStockPriceChange(prevStockPrice, currentStockPrice) if change < 0 { fmt.Printf("The Stock Price decreased by $%.2f which is %.2f%% of the prev price\n", math.Abs(change), math.Abs(percentChange)) } else { fmt.Printf("The Stock Price increased by $%.2f which is %.2f%% of the prev price\n", change, percentChange) } }
Kết quả
The Stock Price decreased by $10000.00 which is 10.00% of the prev price
Giá trị trả về được đặt tên giúp cải thiện khả năng đọc hiểu của hàm. Sử dụng tên có ý nghĩa giúp cho người dùng hiểu rõ hơn về hàm dùng để làm gì chỉ bằng cách nhìn vào thông tin của hàm.
Các câu lệnh return
trống giúp cho hàm ngắn gọn hơn. Nhưng không nên sử dụng nếu hàm quá dài. Chúng có thể gây ra khó hiểu. Bạn nên chỉ định rõ ràng giá trị trả về trong hàm dài.
Định danh trống
Đôi khi bạn muốn bỏ quả một vài kết quả từ hàm trả về nhiều giá trị.
Ví dụ, giả sử bạn đang sử dụng hàm getStockPriceChange
mà chúng ta định nghĩa ở phần trước, nhưng chỉ quan tâm đến lượng tiền thay đổi, và không quan tâm tỉ lệ chênh lệch là bao nhiêu.
Bạn có thể chỉ cần khai báo các biến local và lưu tất cả các giá trị được trả về như sau
change, percentChange := getStockPriceChange(prevStockPrice, currentStockPrice)
Nhưng trong trường hợp này, bạn bị ép buộc phải sử dụng biến percentChange
bởi vì Go không cho phép bạn tạo biến mà không sử dụng.
Vậy giải pháp ở đây là gì? Bạn có thể sử dụng định danh trống thay thế
change, _ := getStockPriceChange(prevStockPrice, currentStockPrice)
Định danh trống thường được sử dụng để bảo với Go rằng chúng ta không sử dụng giá trị này.
package main import ( "fmt" "math" ) func getStockPriceChange(prevPrice, currentPrice float64) (float64, float64) { change := currentPrice - prevPrice percentChange := (change / prevPrice) * 100 return change, percentChange } func main() { prevStockPrice := 80000.0 currentStockPrice := 120000.0 change, _ := getStockPriceChange(prevStockPrice, currentStockPrice) if change < 0 { fmt.Printf("The Stock Price decreased by $%.2f\n", math.Abs(change)) } else { fmt.Printf("The Stock Price increased by $%.2f\n", change) } }
Kết quả
The Stock Price increased by $40000.00