+12

Go by Example - Bài 6 - Go Functions

Giới thiệu

Chào các bạn tới với series về Golang, ở bài trước chúng ta đã tìm hiểu về for loop. Ở bài này chúng ta sẽ tìm hiểu function, đây là một cú pháp cơ bản và quan trọng ở trong Go.

image.png

Chúng ta sẽ tìm hiểu function thông qua việc xây dựng một mini game đơn giản là running game. Game của ta như sau, ta có hai người chơi và 10 vòng đua, mỗi vòng thì mỗi người chơi sẽ chạy được một số bước nhất định, sau khi kết thúc 10 vòng số lượng bước của ai nhiều hơn thì người đó thắng.

$ ./running
Enter name for user one: tom
Enter name for user two: alex
========
Ready for round 1 ...
User tom go 9 step
User alex go 5 step
========
Ready for round 2 ...
User tom go 1 step
User alex go 1 step
========
Ready for round 3 ...
User tom go 2 step
User alex go 5 step
========
Ready for round 4 ...
User tom go 4 step
User alex go 3 step
========
Ready for round 5 ...
User tom go 2 step
User alex go 7 step
========
Ready for round 6 ...
User tom go 2 step
User alex go 3 step
========
Ready for round 7 ...
User tom go 1 step
User alex go 5 step
========
Ready for round 8 ...
User tom go 7 step
User alex go 0 step
========
Ready for round 9 ...
User tom go 0 step
User alex go 5 step
========
Ready for round 10 ...
User tom go 9 step
User alex go 0 step
========
User tom win with 37 step

Build Running Game

Tạo một file tên là main.go, ta sẽ bắt đầu với việc cho người dùng nhập vào tên của hai người chơi.

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	var UserOneName, UserTwoName string
	fmt.Print("Enter name for user one: ")
	fmt.Scan(&UserOneName)
	fmt.Print("Enter name for user two: ")
	fmt.Scan(&UserTwoName)
}

Sau đó ta sẽ dùng vòng lập for để giả lập 10 vòng đua.

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	var UserOneName, UserTwoName string
	fmt.Print("Enter name for user one: ")
	fmt.Scan(&UserOneName)
	fmt.Print("Enter name for user two: ")
	fmt.Scan(&UserTwoName)

	UserOneTotalStep, UserTwoTotalStep := 0, 0

	for round := 1; round <= 10; round++ {
		fmt.Println("========")
		fmt.Printf("Ready for round %d ...\n", round)
	}
}

Trong mỗi vòng đua ta sẽ random số bước cho người chơi.

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	var UserOneName, UserTwoName string
	fmt.Print("Enter name for user one: ")
	fmt.Scan(&UserOneName)
	fmt.Print("Enter name for user two: ")
	fmt.Scan(&UserTwoName)

	UserOneTotalStep, UserTwoTotalStep := 0, 0

	for round := 1; round <= 10; round++ {
		fmt.Println("========")
		fmt.Printf("Ready for round %d ...\n", round)

		source := rand.NewSource(time.Now().UnixNano())
		r := rand.New(source)
		step := r.Intn(10)
		fmt.Printf("User %s go %d step\n", UserOneName, step)
		UserOneTotalStep += step

        source = rand.NewSource(time.Now().UnixNano())
		r = rand.New(source)
		step = r.Intn(10)
		fmt.Printf("User %s go %d step\n", UserTwoName, step)
		UserTwoTotalStep += step

		time.Sleep(500 * time.Millisecond)
	}
}

Cuối cùng ta sẽ kiểm tra số bước của người chơi nào lớn hơn thì người đó sẽ thắng.

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	var UserOneName, UserTwoName string
	fmt.Print("Enter name for user one: ")
	fmt.Scan(&UserOneName)
	fmt.Print("Enter name for user two: ")
	fmt.Scan(&UserTwoName)

	UserOneTotalStep, UserTwoTotalStep := 0, 0

	for round := 1; round <= 10; round++ {
		fmt.Println("========")
		fmt.Printf("Ready for round %d ...\n", round)

		source := rand.NewSource(time.Now().UnixNano())
		r := rand.New(source)
		step := r.Intn(10)
		fmt.Printf("User %s go %d step\n", UserOneName, step)
		UserOneTotalStep += step

        source = rand.NewSource(time.Now().UnixNano())
		r = rand.New(source)
		step = r.Intn(10)
		fmt.Printf("User %s go %d step\n", UserTwoName, step)
		UserTwoTotalStep += step

		time.Sleep(500 * time.Millisecond)
	}

	if UserOneTotalStep > UserTwoTotalStep {
		fmt.Printf("User %s win with %d step\n", UserOneName, UserOneTotalStep)
	} else {
		fmt.Printf("User %s win with %d step\n", UserTwoName, UserTwoTotalStep)
	}
}

Code của ta cũng đơn giản, giờ ta sẽ xem với code ở trên thì function sẽ giúp được gì.

Go Functions

Trong đoạn code trên bạn sẽ thấy có đoạn code nó lập lại là.

source := rand.NewSource(time.Now().UnixNano())
r := rand.New(source)
step := r.Intn(10)
fmt.Printf("User %s go %d step\n", UserOneName, step)
UserOneTotalStep += step

Tuy không có gì sai cả nhưng việc lập một đoạn code giống nhau như vậy sẽ khiến code của ta trông dài hơn và khó kiểm soát hơn về sau nếu ta nâng số người chơi lên là 3 hoặc 4. Do đó trong lập trình, những đoạn code có logic giống nhau và cần sử dụng lại nhiều lần ta có thể định nghĩa nó ở một chỗ, và lôi ra nó sử dụng lại. Ta sẽ sử dụng function để làm việc này.

Function là một cú pháp cho phép ta định nghĩa một đoạn logic nhỏ ở một chỗ, và những lần sau nếu muốn sử dụng lại logic đó ta chỉ cần gọi function là được. Cú pháp khai báo function trong Go.

func <function-name>(param 1, param 2, ..., param n) <return-type> {
    // code
}

Ta có 4 phần khi khai báo một function là:

  • func keywork.
  • <function-name> tên của function.
  • (param 1, param 2, ..., param n) định nghĩa các biến ta sẽ truyền vào function.
  • <return-type> kiểu dữ liệu của giá trị mà hàm sẽ trả về.

Trong đó chỉ có func keywork và <function-name> là bắt buộc phải có khi khai báo function.

Ví dụ của function.

func greet() {
    fmt.Println("Good Morning")
}

Function Call

Và để gọi function thì ta sẽ dùng tên của function và theo sau nó là hai dấu (). Ví dụ ta sẽ khai báo và gọi function như sau.

package main 
import "fmt"

// function to add two numbers
func addNumbers() {
  n1 := 1
  n2 := 2

  sum := n1 + n2
  fmt.Println("Sum: ", sum)
}

func main() {
  // function call
  addNumbers()
}

Ở ví dụ trên ta tạo một function tên là addNumbers, ở trong function là định nghĩa hai biến và ta cộng nó lại sau đó ta in ra. Để gọi function này thì ở trong main() ta gọi nó cộng với hai dấu () như sau addNumbers(). Dưới đây là minh họa cách nó hoạt động.

image.png

Image from Programiz

Function Parameters

Với function addNumbers ở trên thì ta gán cứng hai giá trị cho nó, việc này dẫn tới function của ta không được linh hoạt lắm, lúc nào ta gọi thì nó cũng chỉ in ra một số. Do đó để function của ta trở nên linh hoạt hơn thì thay vì định nghĩa biến trong function thì ta sẽ truyền nó qua biến được truyền hàm (được gọi là parameters).

Ví dụ.

package main 
import "fmt"

// function to add two numbers
func addNumbers(n1 int, n2 int) {
  sum := n1 + n2
  fmt.Println("Sum: ", sum)
}

func main() {
  // function call
  addNumbers(5, 6)
}

Ở trên ta sẽ định nghĩa hai biến n1 và n2 ở trong dấu () khi khai báo function, và khi ta gọi function là sẽ truyền hai này vào với giá trị ta muốn addNumbers(5, 6). Minh họa cách hoạt động.

image.png

Image from Programiz

Return Type

Nếu ta muốn function trả về giá trị thì ta sẽ cần khai báo thêm kiểu dữ liệu của giá trị mà function muốn trả về, sau đó ở trong function ta sẽ dùng return keywork để trả về giá trị.

Ví dụ.

package main 
import "fmt"

// function to add two numbers
func addNumbers(n1 int, n2 int) int {
  return n1 + n2
}

func main() {
  // function call
  sum := addNumbers(5, 6)
  fmt.Println("Sum: ", sum)
}

Ở trên ta sẽ dùng return để trả về giá trị cộng lại của hai biến n1 + n2. Minh họa cách hoạt động.

image.png

Image from Programiz

Ta cũng có thể return về nhiều giá trị trong cùng một function. Ví dụ.

package main
import "fmt"

// function definition
func calculate(n1 int, n2 int) (int, int) {
  sum := n1 + n2
  difference := n1 - n2

  // return two values
  return sum, difference
}

func main() {
  // function call
  sum, difference := calculate(21, 13)
  fmt.Println("Sum: ", sum, "Difference: ", difference)
}

Lưu ý nếu ta đã định nghĩa kiểu dữ liệu của giá trị trả về thì ta phải return đúng kiểu dữ liệu đó, nếu không Go sẽ báo lỗi.

Apply to Multiplication Table

Oke, đó là những thứ cơ bản về function mà ta cần biết, giờ ta sẽ áp dụng nó vào trong code của running game, ta sẽ đưa đoạn code.

source := rand.NewSource(time.Now().UnixNano())
r := rand.New(source)
step := r.Intn(10)
fmt.Printf("User %s go %d step\n", UserOneName, step)
UserOneTotalStep += step

Vào trong function. Sửa lại file main.go.

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	var UserOneName, UserTwoName string
	fmt.Print("Enter name for user one: ")
	fmt.Scan(&UserOneName)
	fmt.Print("Enter name for user two: ")
	fmt.Scan(&UserTwoName)

	UserOneTotalStep, UserTwoTotalStep := 0, 0

	for round := 1; round <= 10; round++ {
		fmt.Println("========")
		fmt.Printf("Ready for round %d ...\n", round)

		UserOneTotalStep += UserGoToStep(UserOneName)
		UserTwoTotalStep += UserGoToStep(UserTwoName)

		time.Sleep(500 * time.Millisecond)
	}

	fmt.Println("========")
	if UserOneTotalStep > UserTwoTotalStep {
		fmt.Printf("User %s win with %d step\n", UserOneName, UserOneTotalStep)
	} else {
		fmt.Printf("User %s win with %d step\n", UserTwoName, UserTwoTotalStep)
	}
}

func UserGoToStep(user string) int {
	source := rand.NewSource(time.Now().UnixNano())
	r := rand.New(source)

	step := r.Intn(10)
	fmt.Printf("User %s go %d step\n", user, step)

	return step
}

Kiểm tra chương trình thử nào.

$ go run main.go
Enter name for user one: Aurora     
Enter name for user two: Goship
========
Ready for round 1 ...
User Aurora go 5 step
User Goship go 8 step
========
Ready for round 2 ...
User Aurora go 9 step
User Goship go 5 step
========
Ready for round 3 ...
User Aurora go 8 step
User Goship go 4 step
========
Ready for round 4 ...
User Aurora go 6 step
User Goship go 9 step
========
Ready for round 5 ...
User Aurora go 3 step
User Goship go 3 step
========
Ready for round 6 ...
User Aurora go 7 step
User Goship go 2 step
========
Ready for round 7 ...
User Aurora go 1 step
User Goship go 6 step
========
Ready for round 8 ...
User Aurora go 1 step
User Goship go 2 step
========
Ready for round 9 ...
User Aurora go 2 step
User Goship go 6 step
========
Ready for round 10 ...
User Aurora go 3 step
User Goship go 3 step
========
User Goship win with 48 step

Oke, chường trình ta đã chạy đúng 😁.

Kết luận

Vậy là ta đã tìm hiểu xong về function, các bạn cần nằm về cách khai báo function, cách định nghĩa parameters, return type và cách gọi function. Nếu có thắc mắc hoặc cần giải thích rõ thêm chỗ nào thì các bạn có thể hỏi dưới phần comment.

Mục tìm kiếm đồng đội

Hiện tại thì công ty bên mình, là Hoàng Phúc International, với hơn 30 năm kinh nghiệm trong lĩnh vực thời trang. Và sở hữu trang thương mại điện tử về thời trang lớn nhất Việt Nam. Team công nghệ của HPI đang tìm kiếm đồng đội cho các vị trí như:

Với mục tiêu trong vòng 5 năm tới về mảng công nghệ là:

  • Sẽ có trang web nằm trong top 10 trang web nhanh nhất VN với 20 triệu lượt truy cập mỗi tháng.
  • 5 triệu loyal customers và có hơn 10 triệu transactions mỗi năm.

Team đang xây dựng một hệ thống rất lớn với rất nhiều vấn đề cần giải quyết, và sẽ có rất nhiều bài toán thú vị cho các bạn. Nếu các bạn có hứng thú trong việc xây dựng một hệ thống lớn, linh hoạt, dễ dàng mở rộng, và performance cao với kiến trúc microservices thì hãy tham gia với tụi mình.

Nếu các bạn quan tâm hãy gửi CV ở trong trang tuyển dụng của Hoàng Phúc International hoặc qua email của mình nha [email protected]. Cảm ơn các bạn đã đọc.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.