英文:
How to write a bidirectional mapping in Go?
问题
我正在编写一个简单的控制台游戏,并希望将玩家映射到一个符号。对于两个玩家,我的方法如下:
func playerToString(p player) string {
if p == 0 {
return "X"
}
return "O"
}
func stringToPlayer(s string) player {
if s == "X" {
return 0
}
return 1
}
当然,你也可以使用两个映射,将整数映射到字符串,字符串映射到整数。上述方法和映射方法都可能出错。在Go语言中,是否有更符合惯用方式的写法?也许有一种非iota枚举的方式?
英文:
I am writing a simple console game and would like to map a player to a symbol. With two players my approach looks like this:
func playerToString(p player) string {
if p == 0 {
return "X"
}
return "O"
}
func stringToPlayer(s string) player {
if s == "X" {
return 0
}
return 1
}
Of cause you could also write this as two maps mapping int to string and string to int. Both the above approach and the map approach seem error-prone. Is there a more idiomatic way to write this in go? Maybe some non-iota enum way?
答案1
得分: 2
我会为你翻译以下内容:
我会写一个映射表:
var player2string = map[int]string{
0: "0",
1: "X",
// 等等...
}
然后,我会创建一个函数来以编程方式填充另一个映射表 string2player
。类似这样:
var player2string = map[int]string{
0: "0",
1: "X",
// 等等...
}
var string2player map[string]int = convertMap(player2string)
func convertMap(m map[int]string) map[string]int {
inv := make(map[string]int)
for k, v := range m {
inv[v] = k
}
return inv
}
func main() {
fmt.Println(player2string)
fmt.Println(string2player)
}
英文:
[I assume your example is just minimal and that your actual mapping has more than two options. I also assume you meant bi-directonal mapping]
I would write one map:
var player2string = map[int]string{
0: "0",
1: "X",
// etc...
}
And then would create a function to populate a different map string2player
programmatically. Something like this:
var player2string = map[int]string{
0: "0",
1: "X",
// etc...
}
var string2player map[string]int = convertMap(player2string)
func convertMap(m map[int]string) map[string]int {
inv := make(map[string]int)
for k, v := range m {
inv[v] = k
}
return inv
}
func main() {
fmt.Println(player2string)
fmt.Println(string2player)
}
答案2
得分: 1
除了Eli的答案之外,你还可以进行另外两个更改。你可以将to-symbol函数作为player
类型的方法。由于player的值是整数(从零开始的顺序),你可以使用切片而不是映射来存储整数到符号的映射,这样存储和查找效率更高。
type player int
var playerSymbols = []string{"X", "O", "A", "B", "C", "D", "E", "F", "G", "H"}
func (p player) Symbol() string {
if int(p) < 0 || int(p) >= len(playerSymbols) {
return "?" // 或者抛出异常?
}
return playerSymbols[p]
}
这个方法的签名甚至可以是String() string
,这样它就是一个fmt.Stringer
,对于打印和调试来说非常有用。
英文:
In addition to Eli's answer, two other changes you could make. You could make the to-symbol function a method of the player
type. And because the player values are integers (sequential starting from zero), you can use a slice instead of a map to store the int-to-symbol mapping -- it's a bit more efficient to store and for lookup.
type player int
var playerSymbols = []string{"X", "O", "A", "B", "C", "D", "E", "F", "G", "H"}
func (p player) Symbol() string {
if int(p) < 0 || int(p) >= len(playerSymbols) {
return "?" // or panic?
}
return playerSymbols
}
This method signature could even be String() string
so it's a fmt.Stringer
, which can be useful for printing stuff out and debugging.
答案3
得分: 0
假设您不需要任何特定的映射,并且玩家的整数值具有序列0、1、2、3、...、25,您可以直接生成玩家符号,而不使用映射,如下面的代码片段所示:
type player int
func ToSymbol(p player) string {
return fmt.Sprintf("%c", 'A' + p)
}
func ToPlayer(symbol string) player {
return player([]rune(symbol)[0] - 'A')
}
这段代码可以将玩家的整数值转换为相应的符号,以及将符号转换回玩家的整数值。
英文:
Assuming you don't need any specific mapping and the player integer values have the sequence 0,1,2,3,...,25, you can generate the players symbols directly without using the maps as shown in following snippet :-
type player int
func ToSymbol(p player) string {
return fmt.Sprintf("%c", 'A' + p)
}
func ToPlayer(symbol string) player {
return player([]rune(symbol)[0] - 'A')
}
答案4
得分: 0
对于以后找到这个线程的任何人,我想要一个更符合人体工程学的双向值映射解决方案,所以我创建了一个小模块来实现这个功能。如果有人正在寻找现成的解决方案,请查看以下链接:
https://github.com/graytonio/go-bidirectional-map
myMap := bidmap.NewMap(map[string]int{
"foo": 3,
"bar": 4,
})
fmt.Println(myMap.GetP("foo"))
// 输出: 3 true
fmt.Println(myMap.GetS(3))
// 输出: foo true
编辑:添加了示例代码
英文:
For anyone that finds this thread down the line I wanted a more ergonomic solution for bi-directional value mapping so I created a small module to do this. If anyone is looking for a ready made solution.
https://github.com/graytonio/go-bidirectional-map
myMap := bidmap.NewMap(map[string]int{
"foo": 3,
"bar": 4,
})
fmt.Println(myMap.GetP("foo"))
// Output: 3 true
fmt.Println(myMap.GetS(3))
// Output: foo true
Edit: Added example code
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论