เมื่อทำซ้ำในแผนที่ที่ส่งคืนในโค้ดซึ่งส่งคืนโดยฟังก์ชันหัวข้อคีย์จะไม่ปรากฏตามลำดับ
ฉันจะทำให้คีย์อยู่ในลำดับ / จัดเรียงแผนที่เพื่อให้คีย์เรียงลำดับและค่าสอดคล้องกันได้อย่างไร
คำตอบ:
ไปบล็อก: Go แผนที่ในการดำเนินการมีคำอธิบายที่ดีเยี่ยม
เมื่อทำการวนซ้ำบนแผนที่ที่มีการวนรอบช่วงจะไม่มีการระบุลำดับการวนซ้ำและไม่รับประกันว่าจะเหมือนกันจากการวนซ้ำครั้งหนึ่งไปอีกครั้ง ตั้งแต่ Go 1 รันไทม์จะสุ่มลำดับการวนซ้ำแผนที่เนื่องจากโปรแกรมเมอร์อาศัยลำดับการวนซ้ำที่เสถียรของการนำไปใช้ก่อนหน้านี้ หากคุณต้องการลำดับการทำซ้ำที่เสถียรคุณต้องรักษาโครงสร้างข้อมูลแยกต่างหากที่ระบุคำสั่งนั้น
นี่คือโค้ดตัวอย่างเวอร์ชันแก้ไขของฉัน: 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])
}
}
เอาท์พุต:
Key: 0 Value: b
Key: 1 Value: a
Key: 2 Value: c
keys := make([]int, len(m))
แล้วแทรกด้วยดัชนีkeys[i] = k
แทนappend
ตามข้อกำหนดGoลำดับของการวนซ้ำบนแผนที่ไม่ได้กำหนดไว้และอาจแตกต่างกันไประหว่างการทำงานของโปรแกรม ในทางปฏิบัติไม่เพียง แต่ไม่ได้กำหนด แต่เป็นการสุ่มโดยเจตนา เนื่องจากเคยเป็นสิ่งที่คาดเดาได้และนักพัฒนาภาษา Go ไม่ต้องการให้ผู้คนอาศัยพฤติกรรมที่ไม่ระบุรายละเอียดดังนั้นพวกเขาจึงสุ่มตัวอย่างโดยเจตนาเพื่อให้การพึ่งพาพฤติกรรมนี้เป็นไปไม่ได้
สิ่งที่คุณจะต้องทำคือดึงคีย์ออกเป็นชิ้น ๆ เรียงลำดับจากนั้นวางช่วงบนชิ้นดังนี้:
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
}
คำตอบทั้งหมดในที่นี้มีพฤติกรรมเก่าของแผนที่ ใน Go 1.12+ คุณสามารถพิมพ์ค่าแผนที่และจะจัดเรียงตามคีย์โดยอัตโนมัติ สิ่งนี้ถูกเพิ่มเข้ามาเนื่องจากช่วยให้สามารถทดสอบค่าแผนที่ได้อย่างง่ายดาย
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]
}
ขณะนี้แผนที่ได้รับการพิมพ์ตามลำดับคีย์เพื่อความสะดวกในการทดสอบ กฎการสั่งซื้อคือ:
- เมื่อทำได้ศูนย์จะเปรียบเทียบค่าต่ำ
- ints ลอยและสตริงเรียงลำดับตาม <
- NaN เปรียบเทียบน้อยกว่าการลอยตัวที่ไม่ใช่ NaN
- บูลเปรียบเทียบเท็จก่อนจริง
- คอมเพล็กซ์เปรียบเทียบของจริงตามด้วยจินตภาพ
- ตัวชี้เปรียบเทียบตามที่อยู่เครื่อง
- ค่าช่องจะเปรียบเทียบตามที่อยู่เครื่อง
- โครงสร้างเปรียบเทียบแต่ละเขตข้อมูลในทางกลับกัน
- อาร์เรย์เปรียบเทียบแต่ละองค์ประกอบในทางกลับกัน
- ค่าอินเทอร์เฟซเปรียบเทียบก่อนโดยการไตร่ตรองพิมพ์อธิบายประเภทคอนกรีตแล้วตามด้วยมูลค่าคอนกรีตตามที่อธิบายไว้ในกฎก่อนหน้า
<nil>
เมื่อพิมพ์แผนที่ค่าคีย์ที่ไม่สะท้อนเช่นน่านมีการแสดงผลก่อนหน้านี้เป็น ในรุ่นนี้จะมีการพิมพ์ค่าที่ถูกต้อง
ในการตอบกลับเจมส์เครกเบอร์ลีย์ของคำตอบ เพื่อให้การออกแบบที่สะอาดและสามารถนำกลับมาใช้ใหม่ได้เราอาจเลือกแนวทางที่เน้นวัตถุมากกว่า วิธีนี้สามารถผูกมัดกับประเภทของแผนที่ที่ระบุได้อย่างปลอดภัย สำหรับฉันวิธีนี้ให้ความรู้สึกสะอาดและเป็นระเบียบ
ตัวอย่าง:
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])
}
}
ตัวอย่างสนามเด็กเล่นแบบขยายที่มีแผนที่หลายประเภท
ในทุกกรณีแผนที่และชิ้นส่วนที่เรียงลำดับจะถูกแยกออกจากช่วงเวลาที่การfor
วนซ้ำบนแผนที่range
เสร็จสิ้น หมายความว่าหากแผนที่ได้รับการแก้ไขหลังจากตรรกะการจัดเรียง แต่ก่อนที่คุณจะใช้คุณอาจประสบปัญหาได้ (ไม่ด้าย / ไปประจำอย่างปลอดภัย) หากมีการเปลี่ยนแปลงการเข้าถึงการเขียนแผนที่แบบขนานคุณจะต้องใช้ mutex รอบ ๆ การเขียนและการfor
วนซ้ำที่เรียงลำดับ
mutex.Lock()
for _, k := range m.sort() {
fmt.Println(m[k])
}
mutex.Unlock()
ถ้าเช่นฉันคุณพบว่าคุณต้องการรหัสการเรียงลำดับเดียวกันเป็นหลักในมากกว่าหนึ่งที่หรือเพียงแค่ต้องการลดความซับซ้อนของโค้ดคุณสามารถแยกการเรียงลำดับตัวเองไปยังฟังก์ชันแยกต่างหากซึ่งคุณส่งผ่านฟังก์ชันที่ทำ งานจริงที่คุณต้องการ (ซึ่งแน่นอนว่าจะแตกต่างกันไปในแต่ละไซต์การโทร)
ด้วยแผนที่ที่มีประเภทคีย์K
และประเภทค่าV
ซึ่งแสดงเป็น<K>
และ<V>
ด้านล่างฟังก์ชันการจัดเรียงทั่วไปอาจมีลักษณะคล้ายกับเทมเพลต Go-code นี้ (ซึ่ง Go เวอร์ชัน 1 ไม่รองรับตามที่เป็นอยู่):
/* 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)
}
}
จากนั้นเรียกใช้ด้วยแผนที่อินพุตและฟังก์ชัน (รับ(k <K>, v <V>)
อาร์กิวเมนต์อินพุต) ที่เรียกใช้บนองค์ประกอบแผนที่ตามลำดับคีย์ที่เรียงลำดับ
ดังนั้นเวอร์ชันของรหัสในคำตอบที่ Mingu โพสต์อาจมีลักษณะดังนี้:
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) })
}
sortedMapIntString()
ฟังก์ชั่นที่สามารถนำมาใช้สำหรับการใด ๆmap[int]string
(สมมติว่าการเรียงลำดับเดียวกันคือต้องการ) ทำให้การใช้งานแต่ละที่จะเพียงแค่สองบรรทัดของรหัส
ข้อเสีย ได้แก่ :
ภาษาอื่น ๆ มีวิธีแก้ปัญหามากมาย:
<K>
และ<V>
(เพื่อแสดงถึงประเภทของคีย์และค่า) ดูคุ้นเคยเล็กน้อยเทมเพลตโค้ดนั้นจะไม่แตกต่างจากเทมเพลต C ++ มากนักrange
ประเภทเฟิร์สคลาสเพื่อให้สามารถแทนที่ด้วยแบบกำหนดเองได้ordered-range
(แทนที่range
โค้ดดั้งเดิม) แต่ฉันคิดว่าภาษาอื่น ๆ บางภาษามีตัวทำซ้ำที่มีประสิทธิภาพเพียงพอที่จะทำสิ่งเดียวกันให้สำเร็จ สิ่ง.var m map[interface{}]interface{}
ถูกกฎหมายอย่างสมบูรณ์ Playground
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
แต่จริงๆแล้วความคิดเห็นบล็อกรัฐ หาก (นอกเหนือจากการใช้unsafe
หรือคล้ายกัน) คุณสามารถแสดงให้เห็นว่าสามารถแทนที่ประเภทที่กำหนดเองได้อย่างไรในขณะรันไทม์ดังนั้นการจัดเรียงรูทีนการเรียงทั่วไปเพียงครั้งเดียวจะเป็นการดีมาก! (ฉันคิดไม่ออกเองเมื่อหลายเดือนก่อน)
นี่เป็นตัวอย่างโค้ดในการจัดเรียงแผนที่ โดยทั่วไปนี่คือสิ่งที่พวกเขาให้:
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
และนี่คือสิ่งที่ฉันขอแนะนำให้ใช้แทน :
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