For
Go chỉ có một cấu trúc lặp duy nhất, đó là vòng lặp for.
Vòng lặp for cơ bản gồm ba thành phần, ngăn cách nhau bằng dấu chấm phẩy:
- câu lệnh khởi tạo: chạy trước lần lặp đầu tiên
- biểu thức điều kiện: được tính trước mỗi lần lặp
- câu lệnh kết thúc vòng: chạy ở cuối mỗi lần lặp
Câu lệnh khởi tạo thường là một khai báo biến, và các biến khai báo ở đó chỉ nhìn thấy được trong phạm vi của câu lệnh for.
Vòng lặp sẽ dừng ngay khi điều kiện kiểu boolean cho ra false.
Lưuý:* Khác với các ngôn ngữ như C, Java hay JavaScript, ba thành phần của câu lệnh for không được bọc trong dấu ngoặc tròn, còn cặp ngoặc nhọn {}` thì luôn bắt buộc.
package main
import "fmt"
func main() {
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
}
Chạy thử ví dụ này trong Tour of Go ↗
For (tiếp theo)
Câu lệnh khởi tạo và câu lệnh kết thúc vòng là tuỳ chọn.
package main
import "fmt"
func main() {
sum := 1
for ; sum < 1000; {
sum += sum
}
fmt.Println(sum)
}
Chạy thử ví dụ này trong Tour of Go ↗
For chính là “while” của Go
Đến đây bạn có thể bỏ luôn các dấu chấm phẩy: while của C được viết là for trong Go.
package main
import "fmt"
func main() {
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
}
Chạy thử ví dụ này trong Tour of Go ↗
Lặp mãi mãi
Nếu bạn bỏ điều kiện lặp thì vòng lặp chạy mãi mãi, nên một vòng lặp vô hạn được viết rất gọn.
package main
func main() {
for {
}
}
Chạy thử ví dụ này trong Tour of Go ↗
If
Câu lệnh if trong Go giống vòng lặp for của nó; biểu thức không cần được bọc trong dấu ngoặc tròn (), nhưng cặp ngoặc nhọn {} thì bắt buộc.
package main
import (
"fmt"
"math"
)
func sqrt(x float64) string {
if x < 0 {
return sqrt(-x) + "i"
}
return fmt.Sprint(math.Sqrt(x))
}
func main() {
fmt.Println(sqrt(2), sqrt(-4))
}
Chạy thử ví dụ này trong Tour of Go ↗
If kèm một câu lệnh ngắn
Giống for, câu lệnh if có thể bắt đầu bằng một câu lệnh ngắn chạy trước khi xét điều kiện.
Các biến khai báo trong câu lệnh đó chỉ tồn tại cho tới hết khối if.
(Hãy thử dùng v trong câu lệnh return cuối cùng.)
package main
import (
"fmt"
"math"
)
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
}
return lim
}
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}
Chạy thử ví dụ này trong Tour of Go ↗
If và else
Các biến khai báo trong câu lệnh ngắn của if cũng dùng được bên trong mọi khối else.
(Cả hai lời gọi pow đều trả về kết quả trước khi lời gọi fmt.Println trong main bắt đầu.)
package main
import (
"fmt"
"math"
)
func pow(x, n, lim float64) float64 {
if v := math.Pow(x, n); v < lim {
return v
} else {
fmt.Printf("%g >= %g\n", v, lim)
}
// can't use v here, though
return lim
}
func main() {
fmt.Println(
pow(3, 2, 10),
pow(3, 3, 20),
)
}
Chạy thử ví dụ này trong Tour of Go ↗
Bài tập: Vòng lặp và hàm
Để nghịch thử với hàm và vòng lặp, hãy cùng hiện thực một hàm căn bậc hai: cho một số x, ta muốn tìm số z sao cho z² gần x nhất.
Máy tính thường tính căn bậc hai của x bằng một vòng lặp. Bắt đầu với một phỏng đoán z nào đó, ta có thể điều chỉnh z dựa trên việc z² gần x tới đâu, để có một phỏng đoán tốt hơn:
z -= (z*z - x) / (2*z)
Lặp lại phép điều chỉnh này khiến phỏng đoán ngày càng tốt hơn, cho tới khi ta đạt tới một kết quả gần với căn bậc hai thật nhất có thể.
Hãy hiện thực điều này trong hàm func Sqrt đã cho sẵn. Một phỏng đoán khởi đầu khá ổn cho z là 1, bất kể giá trị đầu vào là gì. Trước hết, hãy lặp phép tính 10 lần và in ra từng giá trị z trên đường đi. Xem bạn tiến gần tới đáp án đến mức nào với các giá trị x khác nhau (1, 2, 3, …) và phỏng đoán được cải thiện nhanh ra sao.
Gợi ý: Để khai báo và khởi tạo một giá trị dấu phẩy động, hãy viết nó theo cú pháp dấu phẩy động hoặc dùng một phép chuyển kiểu:
z := 1.0
z := float64(1)
Tiếp theo, hãy đổi điều kiện của vòng lặp để nó dừng khi giá trị đã thôi thay đổi (hoặc chỉ thay đổi một lượng rất nhỏ). Xem thử số lần lặp khi đó nhiều hay ít hơn 10. Thử các phỏng đoán khởi đầu khác cho z, ví dụ x, hay x/2. Kết quả của hàm bạn viết gần tới mức nào so với math.Sqrt trong thư viện chuẩn?
(Lưuý:* Nếu bạn quan tâm tới chi tiết của thuật toán, phần z² − x ở trên cho biết z² còn cách nơi nó cần tới (x) bao xa, còn phép chia cho 2z là đạo hàm của z², dùng để chỉnh lượng điều chỉnh z theo tốc độ thay đổi của z². Cách tiếp cận tổng quát này gọi là phương pháp Newton. Nó chạy tốt với nhiều hàm, nhưng đặc biệt tốt với căn bậc hai.)
package main
import (
"fmt"
)
func Sqrt(x float64) float64 {
}
func main() {
fmt.Println(Sqrt(2))
}
Chạy thử ví dụ này trong Tour of Go ↗
Switch
Câu lệnh switch (câu lệnh chọn nhánh theo giá trị) là cách viết ngắn gọn cho một chuỗi câu lệnh if-else. Nó chạy case (nhánh) đầu tiên có giá trị bằng với biểu thức điều kiện.
Switch của Go giống như trong C, C++, Java, JavaScript và PHP, chỉ khác là Go chỉ chạy đúng case được chọn, chứ không chạy tiếp mọi case phía sau. Nói cách khác, câu lệnh break mà các ngôn ngữ kia cần đặt ở cuối mỗi case thì Go tự thêm vào sẵn cho bạn. Một khác biệt quan trọng nữa là các case trong switch của Go không nhất thiết phải là hằng số, và các giá trị liên quan cũng không nhất thiết là số nguyên.
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("macOS.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.\n", os)
}
}
Chạy thử ví dụ này trong Tour of Go ↗
Thứ tự đánh giá của switch
Switch xét các case từ trên xuống dưới, và dừng lại khi có một case khớp.
(Ví dụ,
switch i {
case 0:
case f():
}
sẽ không gọi f nếu i==0.)
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("When's Saturday?")
today := time.Now().Weekday()
switch time.Saturday {
case today + 0:
fmt.Println("Today.")
case today + 1:
fmt.Println("Tomorrow.")
case today + 2:
fmt.Println("In two days.")
default:
fmt.Println("Too far away.")
}
}
Chạy thử ví dụ này trong Tour of Go ↗
Switch không có điều kiện
Switch không kèm điều kiện thì tương đương với switch true.
Cấu trúc này là một cách gọn gàng để viết các chuỗi if-then-else dài.
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
}
Chạy thử ví dụ này trong Tour of Go ↗
Defer
Câu lệnh defer (hoãn thực thi) hoãn việc chạy một hàm lại cho tới khi hàm bao quanh nó trả về.
Các đối số của lời gọi được defer sẽ được tính ngay lập tức, nhưng bản thân lời gọi hàm thì không chạy cho tới khi hàm bao quanh trả về.
package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
Chạy thử ví dụ này trong Tour of Go ↗
Xếp chồng nhiều defer
Các lời gọi hàm được defer sẽ được đẩy vào một ngăn xếp. Khi một hàm trả về, các lời gọi được defer của nó chạy theo thứ tự vào sau ra trước.
Để tìm hiểu thêm về câu lệnh defer, hãy đọc bài viết trên blog này.
package main
import "fmt"
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
Chạy thử ví dụ này trong Tour of Go ↗
Chúc mừng!
Bạn đã hoàn thành bài học này!
Bạn có thể quay lại danh sách các mô-đun để tìm nội dung học tiếp theo, hoặc đi tiếp tới bài học kế tiếp.