Khi lặp lại bản đồ được trả về trong mã, được trả về bởi hàm chủ đề, các phím sẽ không xuất hiện theo thứ tự.
Làm cách nào để lấy các phím theo thứ tự / sắp xếp bản đồ để các phím có thứ tự và các giá trị tương ứng?
Đây là mã .
Khi lặp lại bản đồ được trả về trong mã, được trả về bởi hàm chủ đề, các phím sẽ không xuất hiện theo thứ tự.
Làm cách nào để lấy các phím theo thứ tự / sắp xếp bản đồ để các phím có thứ tự và các giá trị tương ứng?
Đây là mã .
Câu trả lời:
Các Go blog: Go bản đồ trong hành động có một lời giải thích tuyệt vời.
Khi lặp qua bản đồ có vòng lặp phạm vi, thứ tự lặp không được chỉ định và không được đảm bảo là giống nhau từ lần lặp này sang lần lặp tiếp theo. Vì Lượt đi 1, thời gian chạy ngẫu nhiên hóa thứ tự lặp lại bản đồ, vì các lập trình viên dựa vào thứ tự lặp lại ổn định của lần triển khai trước đó. Nếu bạn yêu cầu một thứ tự lặp lại ổn định, bạn phải duy trì một cấu trúc dữ liệu riêng biệt xác định thứ tự đó.
Đây là phiên bản mã mẫu đã sửa đổi của tôi: http://play.golang.org/p/dvqcGPYy3-
package main
import (
"fmt"
"sort"
)
func main() {
// To create a map as input
m := make(map[int]string)
m[1] = "a"
m[2] = "c"
m[0] = "b"
// To store the keys in slice in sorted order
keys := make([]int, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
sort.Ints(keys)
// To perform the opertion you want
for _, k := range keys {
fmt.Println("Key:", k, "Value:", m[k])
}
}
Đầu ra:
Key: 0 Value: b
Key: 1 Value: a
Key: 2 Value: c
keys := make([]int, len(m))
và sau đó chèn bằng chỉ mục keys[i] = k
thay vìappend
Theo thông số kỹ thuật của Go , thứ tự lặp lại trên bản đồ là không xác định và có thể khác nhau giữa các lần chạy chương trình. Trong thực tế, nó không chỉ là không xác định mà còn thực sự được ngẫu nhiên có chủ đích. Điều này là do nó từng có thể dự đoán được và các nhà phát triển ngôn ngữ Go không muốn mọi người dựa vào hành vi không xác định, vì vậy họ cố ý ngẫu nhiên hóa nó để không thể dựa vào hành vi này.
Sau đó, những gì bạn sẽ phải làm là kéo các phím thành một lát, sắp xếp chúng và sau đó phạm vi trên lát như thế này:
var m map[keyType]valueType
keys := sliceOfKeys(m) // you'll have to implement this
for _, k := range keys {
v := m[k]
// k is the key and v is the value; do your computation here
}
Tất cả các câu trả lời ở đây hiện đều chứa hành vi cũ của bản đồ. Trong Go 1.12+, bạn chỉ có thể in một giá trị bản đồ và nó sẽ được sắp xếp theo khóa tự động. Điều này đã được thêm vào vì nó cho phép kiểm tra các giá trị bản đồ một cách dễ dàng.
func main() {
m := map[int]int{3: 5, 2: 4, 1: 3}
fmt.Println(m)
// In Go 1.12+
// Output: map[1:3 2:4 3:5]
// Before Go 1.12 (the order was undefined)
// map[3:5 2:4 1:3]
}
Bản đồ hiện được in theo thứ tự được sắp xếp theo khóa để dễ kiểm tra. Các quy tắc đặt hàng là:
- Khi áp dụng, nil so sánh thấp
- ints, float và string sắp xếp theo thứ tự <
- NaN so sánh ít hơn NaN float
- bool so sánh false trước true
- Phức tạp so sánh giữa thực và ảo
- Con trỏ so sánh theo địa chỉ máy
- Giá trị kênh so sánh theo địa chỉ máy
- Các cấu trúc lần lượt so sánh từng trường
- Các mảng lần lượt so sánh từng phần tử
- Các giá trị giao diện được so sánh trước tiên bằng phản xạ.
Khi in bản đồ, các giá trị khóa không phản xạ như NaN trước đây được hiển thị dưới dạng
<nil>
. Kể từ bản phát hành này, các giá trị chính xác được in.
Đọc thêm tại đây .
Nếu, giống như tôi, bạn thấy về cơ bản bạn muốn có cùng một mã sắp xếp ở nhiều nơi hoặc chỉ muốn giảm độ phức tạp của mã, bạn có thể trừu tượng hóa bản thân việc sắp xếp thành một hàm riêng biệt, mà bạn chuyển hàm đó công việc thực tế bạn muốn (tất nhiên sẽ khác nhau ở mỗi địa chỉ cuộc gọi).
Với một bản đồ có loại khóa K
và loại giá trị V
, được biểu diễn dưới dạng <K>
và <V>
bên dưới, hàm sắp xếp thông thường có thể trông giống như mẫu mã Go này (mà Go phiên bản 1 không hỗ trợ nguyên trạng):
/* Go apparently doesn't support/allow 'interface{}' as the value (or
/* key) of a map such that any arbitrary type can be substituted at
/* run time, so several of these nearly-identical functions might be
/* needed for different key/value type combinations. */
func sortedMap<K><T>(m map[<K>]<V>, f func(k <K>, v <V>)) {
var keys []<K>
for k, _ := range m {
keys = append(keys, k)
}
sort.Strings(keys) # or sort.Ints(keys), sort.Sort(...), etc., per <K>
for _, k := range keys {
v := m[k]
f(k, v)
}
}
Sau đó, gọi nó với bản đồ đầu vào và một hàm (lấy (k <K>, v <V>)
làm đối số đầu vào của nó) được gọi trên các phần tử bản đồ theo thứ tự khóa được sắp xếp.
Vì vậy, một phiên bản của mã trong câu trả lời do Mingu đăng có thể trông giống như sau:
package main
import (
"fmt"
"sort"
)
func sortedMapIntString(m map[int]string, f func(k int, v string)) {
var keys []int
for k, _ := range m {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
f(k, m[k])
}
}
func main() {
// Create a map for processing
m := make(map[int]string)
m[1] = "a"
m[2] = "c"
m[0] = "b"
sortedMapIntString(m,
func(k int, v string) { fmt.Println("Key:", k, "Value:", v) })
}
Các sortedMapIntString()
chức năng có thể được tái sử dụng cho bất kỳ map[int]string
(giả định thứ tự sắp xếp tương tự được mong muốn), giữ mỗi lần sử dụng để chỉ hai dòng mã.
Nhược điểm bao gồm:
Các ngôn ngữ khác có các giải pháp khác nhau:
<K>
và <V>
(để biểu thị các kiểu cho khóa và giá trị) có vẻ hơi quen thuộc, thì mẫu mã đó không giống như các mẫu C ++.range
một loại hạng nhất để nó có thể được thay thế bằng một tùy chỉnh ordered-range
(thay range
cho mã gốc), tôi nghĩ rằng một số ngôn ngữ khác cung cấp trình vòng lặp đủ mạnh để thực hiện điều tương tự Điều.In reply to James Craig Burley của câu trả lời . Để tạo ra một thiết kế sạch sẽ và có thể tái sử dụng, người ta có thể chọn cách tiếp cận hướng đối tượng hơn. Bằng cách này, các phương thức có thể được ràng buộc một cách an toàn với các loại bản đồ được chỉ định. Đối với tôi, cách tiếp cận này cảm thấy sạch sẽ và có tổ chức.
Thí dụ:
package main
import (
"fmt"
"sort"
)
type myIntMap map[int]string
func (m myIntMap) sort() (index []int) {
for k, _ := range m {
index = append(index, k)
}
sort.Ints(index)
return
}
func main() {
m := myIntMap{
1: "one",
11: "eleven",
3: "three",
}
for _, k := range m.sort() {
fmt.Println(m[k])
}
}
Ví dụ về sân chơi mở rộng với nhiều loại bản đồ.
Trong mọi trường hợp, bản đồ và lát cắt đã sắp xếp được tách ra từ thời điểm kết thúc for
vòng lặp trên bản đồ range
. Có nghĩa là, nếu bản đồ được sửa đổi sau logic sắp xếp, nhưng trước khi bạn sử dụng nó, bạn có thể gặp rắc rối. (Không phải luồng / Đi theo quy trình an toàn). Nếu có sự thay đổi về quyền truy cập ghi Bản đồ song song, bạn sẽ cần sử dụng mutex xung quanh các lần ghi và for
vòng lặp được sắp xếp .
mutex.Lock()
for _, k := range m.sort() {
fmt.Println(m[k])
}
mutex.Unlock()
Điều này cung cấp cho bạn ví dụ mã trên bản đồ sắp xếp. Về cơ bản đây là những gì họ cung cấp:
var keys []int
for k := range myMap {
keys = append(keys, k)
}
sort.Ints(keys)
// Benchmark1-8 2863149 374 ns/op 152 B/op 5 allocs/op
và đây là những gì tôi sẽ đề xuất sử dụng thay thế :
keys := make([]int, 0, len(myMap))
for k := range myMap {
keys = append(keys, k)
}
sort.Ints(keys)
// Benchmark2-8 5320446 230 ns/op 80 B/op 2 allocs/op
Bạn có thể tìm thấy mã đầy đủ trong Sân chơi cờ vây này .