【问题标题】:How to write a bidirectional mapping in Go?如何在 Go 中编写双向映射?
【发布时间】:2021-06-22 01:16:43
【问题描述】:

我正在编写一个简单的控制台游戏,并且想将玩家映射到一个符号。对于两个玩家,我的方法如下所示:

func playerToString(p player) string {
    if p == 0 {
        return "X"
    }
    return "O"
}

func stringToPlayer(s string) player {
    if s == "X" {
        return 0
    }
    return 1
}

当然,你也可以把它写成两个映射,映射 int 到 string 和 string 到 int。上述方法和地图方法似乎都容易出错。有没有更惯用的方式来写这个?也许是一些非 iota 枚举方式?

【问题讨论】:

  • 这是一个基于观点的问题,因此可能题外话。
  • 也就是说,一般来说,在每种语言中都可以考虑使用 switch 语句来处理这类事情。
  • 不确定这是否基于意见。可能有一个明确的指南如何做到这一点@BadZen
  • 作为题外话结束似乎有点草率。 OP 提出了一个合理的问题,在 Go 中有两个或三个答案,其中一些在某些情况下可能比其他情况更合适或更惯用。 @User12547645,你能给我们更多的背景信息吗?什么是“播放器”?会不会只有两个玩家,等等?
  • 让我们再等几个小时。也许这个问题会有一个清晰的地道答案

标签: go mapping


【解决方案1】:

[我假设您的示例只是最小的,并且您的实际映射有两个以上的选项。我还假设您的意思是双向映射]

我会写一张地图:

var player2string = map[int]string{
  0: "0",
  1: "X",
  // etc...
}

然后将创建一个函数以编程方式填充不同的地图string2player。像这样的:

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】:

    除了 Eli 的回答之外,您还可以进行另外两项更改。您可以使 to-symbol 函数成为 player 类型的方法。而且由于玩家值是整数(从零开始顺序),您可以使用切片而不是映射来存储 int 到符号的映射——存储和查找效率更高。

    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[p]
    }
    

    这个方法签名甚至可以是String() string,所以它是fmt.Stringer,这对于打印和调试很有用。

    【讨论】:

    • 谢谢。我喜欢fmt.Stringer 选项。这真的很有道理。
    【解决方案3】:

    假设您不需要任何特定的映射并且玩家整数值的序列为 0,1,2,3,...,25,您可以直接生成玩家符号,而无需使用以下 sn 所示的映射-p :-

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

    【讨论】: