2010-08-02 31 views
39

Tôi đến từ nền C++ và tôi đã quen sử dụng lớp std::vector cho những thứ như thế này. Giả sử tôi muốn có một mảng động trong số này:Cách triển khai các mảng có thể thay đổi kích thước trong Go

type a struct { 
    b int 
    c string 
} 

Cách tiêu chuẩn để làm điều này là gì?

Một đoạn sẽ rất hữu ích

+0

chuyên sâu đọc: https://blog.golang.org/go-slices-usage-and-internals – user

Trả lời

42

Sử dụng append() BUILTIN

Ví dụ:

type mytype struct { 
    a, b int 
} 

func main() { 
    a := []mytype{mytype{1, 2}, mytype{3, 4}} 
    a = append(a, mytype{5, 6}) 
} 

Tham khảo spec để biết thêm về append.

+0

hơn Một điều cthom06, nếu bạn sẽ: Tôi đang sử dụng "cho _, t = phạm vi (thông điệp)" trong đó thông điệp là một vectơ. Sau đó tôi phải cast t vào cấu trúc msg của mình. Tôi có thể làm điều này tất cả bên trong tuyên bố trên 1 dòng? –

+0

@ tm1rbrt Tôi không tin như vậy. Ít nhất không phải với phạm vi. – cthom06

+2

Lưu ý rằng gói chứa/gói vé đã biến mất. Nó đã được thay thế bởi các lát vani và chức năng nối thêm. Slices cơ bản là các mảng có thể thay đổi kích thước. –

1

bạn cũng có thể thực hiện với một lát. là một mảng biết chiều dài hiện tại của nó. Và có thể có chiều dài và công suất tối đa riêng biệt. Lưu ý rằng các giá trị được truyền cho kích thước ban đầu và dung lượng không phải là các hằng số để bạn có thể tạo một hàm để tạo và trả về các lát có độ dài khác nhau dựa trên các tham số của nó.

Phía lên là một lát [] Int chỉ có thể được lập chỉ mục như một mảng và sẽ trả về int khi được sử dụng theo cách này.

Nhược điểm là nó sẽ không tự động phát triển vượt qua khả năng đã nêu của nó. Effective Go có một ví dụ về cách bạn sẽ đi về xử lý phân bổ lại.

mã sẽ

type mytype struct { 
    a, b int 
} 




func main() { 

    sl := make([]mytype, 10, 50) //slice of 10 items, max capacity 50 these do not have to be constant expressions. 
    sl[0] = mytype{1,2} 
    //... 
    for i, value := range sl { 
    // ... do stuff with value 
    } 
} 
47

Một Go Slice chứa ba yếu tố: dữ liệu, độ dài và năng lực.

s := make([]int, 0, 10) 

Biến s là một lát ints với chiều dài từ 0 và công suất 10 built-in len() và nắp() chức năng cho phép bạn để có được độ dài và năng lực của một lát:

len(s) == 0 
cap(s) == 10 

Để tăng chiều dài của một lát, chỉ cần tái lát:

s = s[0:5] 
// len(s) == 5 
// cap(s) == 10 

Để giảm độ dài, bạn có thể mất một sub-lát:

s = s[0:1] 
// len(s) == 1 

Có một số cách ngắn hơn để gọi làm():

a := make([]int, 10) 
// len(a) == cap(a) == 10 

b := make([]int) 
// len(b) == cap(b) == 0 

Đó là tất cả tốt và tốt, nhưng những gì nếu bạn cần phải tăng chiều dài của lát vượt quá khả năng của nó? Để làm điều đó, bạn cần phân bổ một slice mới và sao chép nội dung của slice cũ sang slice mới. (Chức năng "sao chép" là một built-in.)

t := make([]int, len(s), 20) 
copy(t, s) 

Các Effective Go document mất ví dụ này một chút nữa, thực hiện một chức năng Append đó gắn thêm một miếng khác, thay đổi kích thước nó nếu cần thiết.

Lát được hỗ trợ bởi mảng; khi bạn thực hiện() một lát của một dung lượng cụ thể, một mảng dung lượng đó được phân bổ trong nền. Các slice có hiệu quả trở thành một "con trỏ thông minh" cho mảng đó. Nếu bạn chuyển slice đó (hoặc một subslice của slice đó) đến một hàm khác, nó sẽ được chuyển thành một con trỏ tới cùng một mảng đó. Điều này làm cho các slice phụ rất rẻ để tạo ra - đó là việc phân bổ mảng sao lưu đắt tiền.

Thư viện chuẩn Go bao gồm một số gói chứa - ví dụ: - loại bỏ sự cần thiết phải quản lý thủ công các lát cắt. Sử dụng lát cho tốc độ, và các lớp container phức tạp hơn để thuận tiện. (Nói vậy, tôi vẫn sử dụng lát cho hầu hết mọi thứ.)

Bạn có thể thắc mắc tại sao bạn cần phải giải quyết mọi rắc rối này. Sau khi tất cả, rất nhiều ngôn ngữ cung cấp mảng được điều chỉnh kích thước động như nguyên thủy. Lý do cho điều này là gắn liền với triết lý của Go. Các nhà thiết kế ngôn ngữ không cho biết chính sách phân bổ phù hợp cho chương trình của bạn là gì; thay vào đó, họ cung cấp cho bạn các công cụ bạn cần để xây dựng cấu trúc dữ liệu của riêng bạn.

+0

Nếu bạn tạo lại slice mỗi khi slice phát triển, nó sẽ ăn mất rất nhiều thời gian. Tuy nhiên, nếu bạn tăng gấp đôi dung lượng slice mỗi lần nó vượt quá khả năng của nó, nó có thể sẽ không đòi hỏi nhiều thời gian để chèn các phần tử mới vào slice. 't: = make ([] int, 5, 10) u: = make ([] int, 10, 20) vv ...' – Xeoncross

+0

@Xeoncros, ['append'] (https://golang.org)/pkg/builtin/# append) nội trang đã được hợp lý thông minh về phân bổ thêm không gian; chỉ cần sử dụng nó. –

+0

lưu ý rằng phần này đã lỗi thời: "tăng chiều dài của một lát vượt quá khả năng của nó" ... "bạn cần phân bổ một lát mới và sao chép nội dung của slice cũ sang lát mới". bây giờ 'append()' thực hiện việc tái phân bổ. xem câu trả lời của cthom06 và Jessta. – minghua

14

Cách thành ngữ để thực hiện việc này đã thay đổi. Việc bổ sung các built-in append() chức năng có nghĩa là bạn có thể kéo dài một lát như vậy:

type a struct { 
    b int 
    c string 
} 

func main(){ 
    var mySlice []a 
    mySlice = append(mySlice,a{5,"pizza"}) 
} 

Append() sẽ nối mục trao cho các lát nếu có phòng hoặc mở rộng lát nếu nó không đủ lớn.

Thông tin thêm về append() là ở đây http://golang.org/doc/go_spec.html#Appending_and_copying_slices

Các vấn đề liên quan