Thursday, November 21, 2024

[Golang] Phần 15 – Go Functions

-

1. Tổng quan.

Trong lập trình Go, hàm (functions) là một khái niệm quan trọng và được sử dụng để thực hiện các nhiệm vụ cụ thể.

Dưới đây là một số ý nghĩa quan trọng của hàm trong lập trình Go:

  • Tái sử dụng code: Hàm cho phép bạn viết code một lần và tái sử dụng nó nhiều lần trong chương trình hoặc trong các chương trình khác. Điều này giúp giảm sự lặp lại và tăng tính duyệt trùng trong mã nguồn.
  • Tổ chức mã nguồn: Sử dụng hàm giúp bạn tổ chức mã nguồn thành các khối logic nhỏ và dễ quản lý. Các chức năng cụ thể có thể được triển khai và kiểm thử một cách độc lập.
  • Chia nhỏ vấn đề: Bạn có thể sử dụng hàm để chia nhỏ vấn đề lớn thành các vấn đề nhỏ hơn, giúp dễ dàng quản lý và giữ cho mã nguồn trở nên rõ ràng hơn.
  • Tính tái sử dụng: Hàm làm cho mã nguồn trở nên có thể tái sử dụng. Bạn có thể gọi hàm từ nhiều nơi trong chương trình của mình, giúp tiết kiệm thời gian và công sức.
  • Thực thi điều kiện: Hàm có thể được sử dụng để thực hiện các điều kiện và quyết định trong chương trình. Các hàm có thể trả về giá trị, và kết quả đó có thể được sử dụng để đưa ra quyết định tiếp theo.
  • Hỗ trợ đa luồng (concurrency): Trong Go, hàm cũng có thể được sử dụng để tạo goroutines, đơn vị nhẹ đa luồng, giúp xử lý đa luồng một cách hiệu quả.

2. Cú pháp tạo một Functions.

Trong ngôn ngữ lập trình Go, một hàm (function) được định nghĩa bằng cách sử dụng keyword func. Dưới đây là cú pháp để tạo một hàm trong Go:

Cú pháp:

func FunctionName() {
  // Code để được thực thi khi gọi hàm
}

Trong đó:

  • func: Bắt đầu việc định nghĩa một hàm.
  • FunctionName: Tên của hàm. Đặt tên cho hàm phải tuân theo quy tắc đặt tên trong Go.
  • (): Dấu ngoặc đơn, không có tham số đầu vào cho hàm trong trường hợp này.
  • {}: Dấu ngoặc nhọn, bao quanh phần mã nguồn mà hàm sẽ thực hiện khi được gọi.

Dưới đây là một ví dụ về cách tạo một hàm:

package main

import "fmt"

// Định nghĩa hàm hello
func hello() {
  fmt.Println("Hello, World!")
}

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

Kết quả:

Hello, World!

Trong ví dụ này, hàm hello được định nghĩa để in ra chuỗi “Hello, World!”, và sau đó hàm được gọi từ hàm main.

3. Cách để gọi Functions.

Trong ngôn ngữ lập trình Go, hàm không được thực thi ngay lập tức mà phải được gọi khi cần thiết. Dưới đây là một ví dụ về cách gọi một hàm trong Go:

package main

import "fmt"

// Định nghĩa hàm myMessage
func myMessage() {
  fmt.Println("I just got executed!")
}

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

Kết quả:

I just got executed!

Trong ví dụ này:

  • func myMessage() { fmt.Println("I just got executed!") }: Hàm myMessage được định nghĩa để in ra chuỗi “I just got executed!”.
  • myMessage(): Hàm myMessage được gọi từ hàm main bằng cách sử dụng tên hàm kèm theo dấu ngoặc đơn (). Khi chương trình chạy, hàm myMessage sẽ được thực thi và in ra chuỗi đã được định nghĩa.

Một ví dụ tiếp theo hướng dẫn cách sử dụng hàm để gọi nhiều lần trong chương trình.

package main

import "fmt"

// Định nghĩa hàm myMessage
func myMessage() {
  fmt.Println("I just got executed!")
}

func main() {
  // Gọi hàm myMessage ba lần
  myMessage()
  myMessage()
  myMessage()
}

Kết quả:

I just got executed!
I just got executed!
I just got executed!

Trong ví dụ này, hàm myMessage được gọi ba lần từ hàm main. Mỗi lần hàm được gọi, code bên trong nó sẽ thực thi và in ra chuỗi “I just got executed!”. Do đó, kết quả là chuỗi này được in ra ba lần.

4. Tham số và đối số.

Trong ngôn ngữ lập trình Go, thông tin có thể được truyền vào hàm dưới dạng tham số. Tham số đóng vai trò như các biến bên trong hàm.

Tham số và kiểu dữ liệu của chúng được chỉ định sau tên hàm, bên trong dấu ngoặc đơn. Bạn có thể thêm bao nhiêu tham số tùy ý, chỉ cần phân tách chúng bằng dấu phẩy:

Cú pháp:

func FunctionName(param1 type, param2 type, param3 type) {
  // Code để được thực thi
}

Dưới đây là một ví dụ về cách sử dụng tham số trong hàm:

package main

import "fmt"

// Định nghĩa hàm familyName với tham số fname kiểu string
func familyName(fname string) {
  fmt.Println("Hello", fname, "Refsnes")
}

func main() {
  // Gọi hàm familyName với các đối số khác nhau
  familyName("Liam")
  familyName("Jenny")
  familyName("Anja")
}

Kết quả:

Hello Liam Refsnes
Hello Jenny Refsnes
Hello Anja Refsnes

Trong ví dụ này:

  • func familyName(fname string): Hàm familyName có một tham số là fname kiểu string.
  • familyName("Liam"), familyName("Jenny"), familyName("Anja"): Gọi hàm familyName với các đối số (arguments) khác nhau. Khi hàm được gọi, giá trị của fname trong hàm sẽ được thay thế bằng giá trị của đối số tương ứng. Do đó, kết quả là in ra các câu chào với tên được truyền vào như là một phần của họ gia đình.

Bạn có thể thêm nhiều tham số như bạn muốn bên trong hàm. Dưới đây là một ví dụ về cách sử dụng nhiều tham số trong một hàm:

package main

import "fmt"

// Định nghĩa hàm familyName với tham số fname kiểu string và tham số age kiểu int
func familyName(fname string, age int) {
  fmt.Println("Hello", age, "year old", fname, "Refsnes")
}

func main() {
  // Gọi hàm familyName với các đối số khác nhau
  familyName("Liam", 3)
  familyName("Jenny", 14)
  familyName("Anja", 30)
}

Kết quả.

Hello 3 year old Liam Refsnes
Hello 14 year old Jenny Refsnes
Hello 30 year old Anja Refsnes

Trong ví dụ này:

  • func familyName(fname string, age int): Hàm familyName có hai tham số, là fname kiểu string và age kiểu int.
  • familyName("Liam", 3), familyName("Jenny", 14), familyName("Anja", 30): Gọi hàm familyName với các bộ đối số khác nhau. Khi hàm được gọi, giá trị của fnameage trong hàm sẽ được thay thế bằng giá trị của đối số tương ứng. Do đó, kết quả là in ra các câu chào với tên và tuổi được truyền vào như là một phần của họ gia đình.

5. Sử dụng return để trả về một giá trị trong hàm.

Nếu bạn muốn hàm trả về một giá trị, bạn cần định nghĩa kiểu dữ liệu của giá trị trả về (như int, string, v.v.) và sử dụng return bên trong hàm:

Cú pháp:

func FunctionName(param1 type, param2 type) type {
  // Code để được thực thi
  return output
}

Dưới đây là một ví dụ về cách sử dụng hàm với giá trị trả về:

package main

import "fmt"

// Định nghĩa hàm myFunction với tham số x và y kiểu int, và trả về kiểu int
func myFunction(x int, y int) int {
  return x + y
}

func main() {
  // Gọi hàm myFunction và in ra giá trị trả về
  fmt.Println(myFunction(1, 2))
}

Kết quả:

3

Trong ví dụ này:

  • func myFunction(x int, y int) int: Hàm myFunction nhận hai tham số là xy kiểu int và trả về một giá trị kiểu int.
  • return x + y: Trong hàm, giá trị của x + y được trả về.
  • fmt.Println(myFunction(1, 2)): Gọi hàm myFunction và in ra giá trị trả về (tổng của 1 và 2).

6. Đặt tên cho các giá trị trả về của một hàm.

Bạn có thể đặt tên cho các giá trị trả về của một hàm. Dưới đây là một ví dụ về cách sử dụng giá trị trả về có tên.

package main

import "fmt"

// Định nghĩa hàm myFunction với tham số x và y kiểu int, và giá trị trả về có tên là result kiểu int
func myFunction(x int, y int) (result int) {
  result = x + y
  return
}

func main() {
  // Gọi hàm myFunction và in ra giá trị trả về
  fmt.Println(myFunction(1, 2))
}

Kết quả.

3

Trong ví dụ này:

  • func myFunction(x int, y int) (result int): Hàm myFunction nhận hai tham số là xy kiểu int và trả về một giá trị kiểu int có tên là result.
  • result = x + y: Trong hàm, giá trị của x + y được gán cho biến result.
  • return: Dòng này sử dụng naked return, nghĩa là chúng ta không cần phải ghi rõ tên biến trả về (result). Go tự hiểu và trả về giá trị của result.

7. Dữ liệu trả về viết theo naked return.

Trong ngôn ngữ lập trình Go, naked return là một cách viết ngắn gọn khi bạn muốn trả về giá trị từ một hàm có giá trị trả về đã được đặt tên. Thay vì sử dụng return kèm theo tên biến trả về, bạn có thể chỉ sử dụng return mà không cần ghi rõ tên biến. Go sẽ tự hiểu và trả về giá trị của biến đã được đặt tên.

Ví dụ, đây là một hàm sử dụng naked return:

func calculate(x, y int) (result int) {
  result = x + y
  return
}

Trong ví dụ này, result được đặt tên là giá trị trả về của hàm. Khi sử dụng naked return, bạn có thể viết return mà không cần ghi rõ result. Go sẽ hiểu và trả về giá trị của result:

return

Naked return thường được sử dụng khi hàm có nhiều biến trả về và giúp làm cho mã nguồn ngắn gọn hơn. Tuy nhiên, sự sử dụng của nó cũng phụ thuộc vào sự thoải mái và quy ước trong dự án cụ thể.

Bạn có thể viết ví dụ ở phần 6 phía trên theo cách khác mà không sử dụng naked return, như sau:

package main

import "fmt"

// Định nghĩa hàm myFunction với tham số x và y kiểu int, và giá trị trả về có tên là result kiểu int
func myFunction(x int, y int) (result int) {
  result = x + y
  return result
}

func main() {
  // Gọi hàm myFunction và in ra giá trị trả về
  fmt.Println(myFunction(1, 2))
}

Ở đây, return result được sử dụng để trả về giá trị của biến result. Cả hai cách viết đều hợp lệ, nhưng sự lựa chọn giữa naked return và cách sử dụng return variable thường phụ thuộc vào sự thoải mái và ưa thích của người lập trình.

8. Lưu giá trị trả về từ một hàm vào một biến.

Bạn có thể lưu giá trị trả về từ một hàm vào một biến.

package main

import "fmt"

// Định nghĩa hàm myFunction với tham số x và y kiểu int, và giá trị trả về có tên là result kiểu int
func myFunction(x int, y int) (result int) {
  result = x + y
  return
}

func main() {
  // Gọi hàm myFunction và lưu giá trị trả về vào biến total
  total := myFunction(1, 2)
  fmt.Println(total)
}

Trong ví dụ này, total là một biến được khai báo để lưu giữ giá trị trả về từ hàm myFunction(1, 2). Sau đó, giá trị của total được in ra bằng cách sử dụng fmt.Println(total) trong hàm main.

Kết quả sẽ là:

3

Vì hàm myFunction trả về tổng của 12, nên giá trị của total3.

9. Hàm có thể trả về nhiều giá trị.

Trong Go, hàm có thể trả về nhiều giá trị.

package main

import "fmt"

// Định nghĩa hàm myFunction với tham số x kiểu int và y kiểu string
// Hàm trả về hai giá trị: result kiểu int và txt1 kiểu string
func myFunction(x int, y string) (result int, txt1 string) {
  result = x + x
  txt1 = y + " World!"
  return
}

func main() {
  // Gọi hàm myFunction và in ra hai giá trị trả về
  fmt.Println(myFunction(5, "Hello"))
}

Kết quả:

10 Hello World!

Ở đây, hàm myFunction trả về hai giá trị: result là tổng của x + xtxt1 là chuỗi kết hợp từ tham số y và ” World!”. Trong hàm main, chúng ta in ra cả hai giá trị này.

Ngoài ra, bạn cũng có thể lưu giá trị trả về vào nhiều biến khác nhau, như trong ví dụ dưới đây:

package main

import "fmt"

// Định nghĩa hàm myFunction với tham số x kiểu int và y kiểu string
// Hàm trả về hai giá trị: result kiểu int và txt1 kiểu string
func myFunction(x int, y string) (result int, txt1 string) {
  result = x + x
  txt1 = y + " World!"
  return
}

func main() {
  // Gọi hàm myFunction và lưu hai giá trị trả về vào hai biến a và b
  a, b := myFunction(5, "Hello")
  fmt.Println(a, b)
}

Kết quả:

10 Hello World!

Ở đây, chúng ta sử dụng cú pháp a, b := myFunction(5, "Hello") để lưu giá trị trả về vào hai biến ab.

10. Bỏ qua giá trị trả về.

Bạn có thể sử dụng dấu gạch dưới (_) để bỏ qua giá trị trả về mà bạn không muốn sử dụng.

package main

import "fmt"

// Định nghĩa hàm myFunction với tham số x kiểu int và y kiểu string
// Hàm trả về hai giá trị: result kiểu int và txt1 kiểu string
func myFunction(x int, y string) (result int, txt1 string) {
  result = x + x
  txt1 = y + " World!"
  return
}

func main() {
  // Sử dụng dấu gạch dưới (_) để bỏ qua giá trị trả về đầu tiên và chỉ lưu giá trị thứ hai vào biến b
  _, b := myFunction(5, "Hello")
  fmt.Println(b)
}

Kết quả:

Hello World!

Ở đây, chúng ta sử dụng _ để bỏ qua giá trị trả về đầu tiên và chỉ lưu giá trị thứ hai vào biến b. Điều này hữu ích khi bạn chỉ quan tâm đến một số giá trị trả về cụ thể và không muốn sử dụng tất cả chúng.

11. Bỏ qua một hoặc nhiều giá trị.

Bạn cũng có thể sử dụng dấu gạch dưới (_) để bỏ qua một hoặc nhiều giá trị trả về mà bạn không muốn sử dụng.

package main

import "fmt"

// Định nghĩa hàm myFunction với tham số x kiểu int và y kiểu string
// Hàm trả về hai giá trị: result kiểu int và txt1 kiểu string
func myFunction(x int, y string) (result int, txt1 string) {
  result = x + x
  txt1 = y + " World!"
  return
}

func main() {
  // Sử dụng dấu gạch dưới (_) để bỏ qua giá trị trả về thứ hai và chỉ lưu giá trị thứ nhất vào biến a
  a, _ := myFunction(5, "Hello")
  fmt.Println(a)
}

Kết quả:

10

Ở đây, chúng ta sử dụng _ để bỏ qua giá trị trả về thứ hai và chỉ lưu giá trị trả về thứ nhất vào biến a. Điều này hữu ích khi bạn chỉ quan tâm đến một số giá trị trả về cụ thể và không muốn sử dụng tất cả chúng.

12. Đệ quy trong Go.

Bạn có thể viết hàm đệ quy, tức là một hàm gọi chính nó.

package main

import "fmt"

// Hàm testcount là một hàm đệ quy
func testcount(x int) int {
  // Điều kiện dừng: nếu x == 11, trả về 0
  if x == 11 {
    return 0
  }
  
  // In giá trị của x và gọi lại hàm với tham số x + 1
  fmt.Println(x)
  return testcount(x + 1)
}

func main() {
  // Gọi hàm đệ quy với tham số ban đầu là 1
  testcount(1)
}

Kết quả.

1
2
3
4
5
6
7
8
9
10

Trong ví dụ này, hàm testcount gọi chính nó với tham số tăng dần từ 1 đến 11. Điều kiện dừng là khi x bằng 11, khi đó hàm trả về giá trị 0 và quá trình đệ quy kết thúc. Điều này giúp in ra các giá trị từ 1 đến 10. Đệ quy thường được sử dụng khi giải quyết vấn đề được mô tả bằng cách chia nhỏ vấn đề thành các vấn đề con nhỏ hơn.

13. Lưu ý khi sử dụng hàm đệ quy

Đệ quy là một khái niệm phổ biến trong toán học và lập trình. Điều này có lợi ích là bạn có thể lặp qua dữ liệu để đạt được kết quả.

Tuy nhiên, nhà phát triển cần phải cẩn thận khi sử dụng hàm đệ quy vì có thể dễ dàng viết một hàm không bao giờ kết thúc hoặc một hàm sử dụng quá nhiều bộ nhớ hoặc nguồn xử lý. Tuy nhiên, khi viết đúng, đệ quy có thể là một cách tiếp cận rất hiệu quả và toán học-elegant để lập trình.

Trong ví dụ dưới đây, factorial_recursion() là một hàm đệ quy. Chúng ta sử dụng biến x như là dữ liệu, giảm đi 1 mỗi lần đệ quy. Quá trình đệ quy kết thúc khi điều kiện không còn lớn hơn 0 (tức là khi nó bằng 0).

package main

import "fmt"

// Hàm đệ quy tính giai thừa
func factorial_recursion(x float64) (y float64) {
  if x > 0 {
     y = x * factorial_recursion(x-1)
  } else {
     y = 1
  }
  return
}

func main() {
  // In ra giai thừa của 4
  fmt.Println(factorial_recursion(4))
}

Kết quả:

24

Ở đây, hàm factorial_recursion tính giai thừa của một số bằng cách sử dụng đệ quy. Kết quả là 24, vì giai thừa của 4 là 4 * 3 * 2 * 1 = 24. Tuy nhiên, khi sử dụng đệ quy, nhất quán và hiểu được cách hoạt động là quan trọng.

Bạn có thể tham khảo các ví dụ về Golang tại https://gobyexample.com/.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

4,956FansLike
256FollowersFollow
223SubscribersSubscribe
spot_img

Related Stories