How to write a bidirectional mapping in Go?

huangapple go评论76阅读模式
英文:

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)
}

在 Go playground 上试一试

英文:

[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)
}

Try it on the Go playground

答案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{&quot;X&quot;, &quot;O&quot;, &quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;, &quot;F&quot;, &quot;G&quot;, &quot;H&quot;}

func (p player) Symbol() string {
	if int(p) &lt; 0 || int(p) &gt;= len(playerSymbols) {
		return &quot;?&quot; // 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(&quot;%c&quot;, &#39;A&#39; + p)
}

func ToPlayer(symbol string) player {
    return player([]rune(symbol)[0] - &#39;A&#39;)
}

答案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{
	&quot;foo&quot;: 3,
	&quot;bar&quot;: 4,
})

fmt.Println(myMap.GetP(&quot;foo&quot;))
// Output: 3 true

fmt.Println(myMap.GetS(3))
// Output: foo true

Edit: Added example code

huangapple
  • 本文由 发表于 2021年6月21日 05:06:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/68060034.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定