Go – Hàm

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

 

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.