Có một vòng lặp foreach trong Go?


565

Có một foreachcấu trúc trong ngôn ngữ Go? Tôi có thể lặp lại một lát hoặc mảng bằng cách sử dụng forkhông?



1
Việc sử dụng rangetrong forvòng cũng được đề cập trong "Một Interlude về loại" phần (về phía cuối của nó) của Go hướng dẫn.
kostix

Câu trả lời:


851

https://golang.org/ref/spec#For_range

Câu lệnh "for" với mệnh đề "phạm vi" lặp qua tất cả các mục của một mảng, lát, chuỗi hoặc bản đồ hoặc các giá trị nhận được trên một kênh. Đối với mỗi mục, nó gán các giá trị lặp cho các biến lặp tương ứng và sau đó thực hiện khối.

Ví dụ:

for index, element := range someSlice {
    // index is the index where we are
    // element is the element from someSlice for where we are
}

Nếu bạn không quan tâm đến chỉ mục, bạn có thể sử dụng _:

for _, element := range someSlice {
    // element is the element from someSlice for where we are
}

Gạch dưới, _định danh trống , một trình giữ chỗ ẩn danh.


7
Trong ví dụ này, elementgiá trị của phần tử (bản sao) - bản thân nó không phải là phần tử. Mặc dù bạn có thể gán cho element, nhưng điều này sẽ không ảnh hưởng đến chuỗi bên dưới.
tộc

Tôi biết trong Python và C, người ta thường sử dụng dấu gạch dưới làm hàm để bản địa hóa (tức là gettext ). Việc sử dụng dấu gạch dưới có gây ra bất kỳ vấn đề nào trong Go không? Go thậm chí có sử dụng cùng một thư viện để bản địa hóa không?
Sergiy Kolodyazhnyy

2
Tài liệu @SergiyKolodyazhnyy Py nói rằng hàm "(gettext) thường được đặt bí danh như _()trong không gian tên cục bộ", theo quy ước , nó không phải là một phần của lib nội địa hóa. Dấu gạch dưới _là nhãn hợp lệ và nó cũng quy ước trong Go (và Python và Scala và các lang khác) để gán _cho các giá trị trả về mà bạn sẽ không sử dụng. Phạm vi _trong ví dụ này được giới hạn trong phần thân của forvòng lặp. Nếu bạn có một hàm phạm vi gói _thì nó sẽ bị che khuất bên trong phạm vi của vòng lặp for. Có một vài gói để bản địa hóa, tôi chưa thấy sử dụng _làm tên hàm.
Davos

149

Go có một foreachcú pháp giống như. Nó hỗ trợ mảng / lát, bản đồ và kênh.

Lặp lại trên mảng hoặc lát :

// index and value
for i, v := range slice {}

// index only
for i := range slice {}

// value only
for _, v := range slice {}

Lặp lại trên bản đồ :

// key and value
for key, value := range theMap {}

// key only
for key := range theMap {}

// value only
for _, value := range theMap {}

Lặp lại qua một kênh :

for v := range theChan {}

Lặp lại qua một kênh tương đương với việc nhận từ một kênh cho đến khi nó được đóng lại:

for {
    v, ok := <-theChan
    if !ok {
        break
    }
}

10
Mặc dù OP chỉ yêu cầu sử dụng lát, tôi thích câu trả lời này, vì hầu hết cuối cùng cũng sẽ cần các cách sử dụng khác.
domoarigato

3
phân biệt quan trọng về chancách sử dụng: phạm vi trên một kênh sẽ thoát khỏi vòng lặp một cách duyên dáng nếu người viết đóng kênh tại một số điểm. Trong for {v := <-theChan} tương đương , nó sẽ không thoát trên kênh gần. Bạn có thể kiểm tra điều này thông qua okgiá trị trả lại thứ hai . VÍ DỤ DU LỊCH
colm.anseo

Suy nghĩ tương tự khi đọc nó, for { ... }là viết tắt của một vòng lặp vô hạn.
Levite

13

Ví dụ sau đây cho thấy cách sử dụng rangetoán tử trong một forvòng lặp để thực hiện một foreachvòng lặp.

func PrintXml (out io.Writer, value interface{}) error {
    var data []byte
    var err error

    for _, action := range []func() {
        func () { data, err = xml.MarshalIndent(value, "", "  ") },
        func () { _, err = out.Write([]byte(xml.Header)) },
        func () { _, err = out.Write(data) },
        func () { _, err = out.Write([]byte("\n")) }} {
        action();
        if err != nil {
            return err
        }
    }
    return nil;
}

Ví dụ lặp lại qua một mảng các hàm để thống nhất xử lý lỗi cho các hàm. Một ví dụ hoàn chỉnh là tại sân chơi Google Google .

PS: nó cũng cho thấy rằng niềng răng treo là một ý tưởng tồi cho tính dễ đọc của mã. Gợi ý: forđiều kiện kết thúc ngay trước action()cuộc gọi. Rõ ràng, phải không?


3
Thêm một ,và rõ ràng hơn khi forđiều kiện kết thúc: play.golang.org/p/pcRg6WdxBd - Đây thực sự là lần đầu tiên tôi tìm thấy một đối số go fmttheo kiểu, cảm ơn!
topskip

@topskip cả hai đều hợp lệ; chỉ cần chọn thứ tốt nhất :)
Filip Haglund

@FilipHaglund Không phải là vấn đề nếu nó hợp lệ. Vấn đề là IMO rõ ràng hơn khi điều kiện kết thúc trong trường hợp cụ thể ở trên.
topskip

8
Theo tôi, câu trả lời này phức tạp một cách vô lý cho câu hỏi được nhắm mục tiêu.
AndreasHassing

@AndreasHassing Làm thế nào để làm điều đó thay vào đó mà không giới thiệu dự phòng?
ceving

10

Trong thực tế, bạn có thể sử dụng rangemà không cần tham chiếu các giá trị trả về bằng cách sử dụng for rangeđối với loại của bạn:

arr := make([]uint8, 5)
i,j := 0,0
for range arr {
    fmt.Println("Array Loop",i)
    i++
}

for range "bytes" {
    fmt.Println("String Loop",j)
    j++
}

https://play.golang.org/p/XHrHLbJMEd


3
Điều tốt để biết nhưng điều đó sẽ không hữu ích trong hầu hết các trường hợp
Sridhar

Đồng ý @Sridhar nó khá thích hợp.
robstarbuck

9

Sau đây là mã ví dụ về cách sử dụng foreach trong golang

package main

import (
    "fmt"
)

func main() {

    arrayOne := [3]string{"Apple", "Mango", "Banana"}

    for index,element := range arrayOne{

        fmt.Println(index)
        fmt.Println(element)        

    }   

}

Đây là một ví dụ đang chạy https://play.golang.org/p/LXptmH4X_0


giải thích rất tốt!
Darlan Dieterich

4

Có, Phạm vi :

Dạng phạm vi của vòng lặp for lặp trên một lát hoặc bản đồ.

Khi phạm vi trên một lát, hai giá trị được trả về cho mỗi lần lặp. Đầu tiên là chỉ mục và thứ hai là một bản sao của phần tử tại chỉ mục đó.

Thí dụ :

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }

    for i := range pow {
        pow[i] = 1 << uint(i) // == 2**i
    }
    for _, value := range pow {
        fmt.Printf("%d\n", value)
    }
}
  • Bạn có thể bỏ qua chỉ mục hoặc giá trị bằng cách gán cho _.
  • Nếu bạn chỉ muốn chỉ mục, bỏ hoàn toàn, giá trị.

1

Điều này có thể rõ ràng, nhưng bạn có thể nội tuyến mảng như vậy:

package main

import (
    "fmt"
)

func main() {
    for _, element := range [3]string{"a", "b", "c"} {
        fmt.Print(element)
    }
}

đầu ra:

abc

https://play.golang.org/p/gkKgF3y5nmt

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.