【问题标题】:Workaround for lack of support for array constants? [duplicate]缺乏对数组常量的支持的解决方法? [复制]
【发布时间】:2017-11-06 14:56:53
【问题描述】:

Go 没有数组常量。

我的应用程序接收到包含多种类型的数字代码的消息,我需要将其显示为短语。

如果存在数组内容,我可以执行以下操作:

func foo() {
   ...
   fmt.Println(facename[f])
   ...
}
const facename [...]string = "top", "bottom", "left", "right", "front", "back"

但当然没有办法做到这一点。我想到的第一种解决方法,也许是一个合理有效的方法是使用switch

func foo() {
   ...
   name := "unknown"
   switch f {
   case 0:
      name = "top"
   case 1:
      name = "bottom"
   case 2:
      name = "left"
   case 3:
      name = "right"
   case 4:
      name = "front"
   case 5:
      name = "back"
   }
   fmt.Println(name)
   ...
}

如果值的数量达到 20 或更多,则上述内容相当繁琐。

似乎最简洁的方式是这样的

func foo() {
   ...
   fmt.Println(strings.Split(facenames,",")[f])
   ...
}
const facenames = "top,bottom,left,right,front,back"

当然,我还必须检查索引是否在范围内。 尽管目前效率不是问题,但让我感到困扰的是 我使用 strings.Split() 的次数超出了我的预期。

是否有另一种方式既惯用又简洁高效?

【问题讨论】:

  • 为什么不使用全局变量而不是常量?我的意思是,你没有得到“恒定”保护,但只要不导出变量,无论如何只有你自己的包可能会弄乱它。

标签: arrays go initialization idioms


【解决方案1】:

惯用的方法是使用包级变量:

var facename = []string{"top", "bottom", "left", "right", "front", "back"}

func foo() {
    // ...
    fmt.Println(facename[f])
    // ...
}

在这种情况下使用切片而不是数组也是惯用的。

【讨论】:

    【解决方案2】:

    它可能不太合适,但它似乎你的用例基本上是一个具有字符串等价物的枚举。为此,我通常会按照以下方式做一些事情:

    type Face uint8
    
    const (
        FaceTop Face = iota
        FaceBottom
        FaceLeft
        FaceRight
        FaceFront
        FaceBack
    )
    
    func (f Face) String() string {
        switch f {
        case FaceTop: return "top"
        case FaceBottom: return "bottom"
        case FaceLeft: return "left"
        case FaceRight: return "right"
        case FaceFront: return "front"
        case FaceBack: return "back"
        default: return ""  // Or add an error return, or add a zero value to the enum
        }
    }
    
    func ParseFace(in string) (Face,error) {
        switch in {
        case "top": return FaceTop,nil
        case "bottom": return FaceBottom,nil
        case "left": return FaceLeft,nil
        case "right": return FaceRight,nil
        case "front": return FaceFront,nil
        case "back": return FaceBack,nil
        default: return "",errors.New("invalid face name"+in)
        }
    }
    

    【讨论】:

    • 顺便说一句,这不会编译,因为String() 方法没有保证返回值。如果给定的 Face 不在 iota 列表中(即您手动创建它,如在 var f = Face(25) 中,则不会匹配任何大小写,因此 String() 不会有返回值。编译器会阻止它,因为有没有返回值的有效决策树。
    • 另外,在这个例子中,你的 iotas 不是 Face 类型,所以不能对它们调用 String()。您需要第一行是 FaceTop Face = iota
    • 这两点都不错——第二点是编辑过多。第一个是因为我的枚举通常有一个单独的零值,我可以将其用作半有效的默认情况,例如FaceNone 或其他东西,因此零不等于任何“真实”值,但我不无法全面了解 OP 的用例。
    • 在大多数情况下,我实际上将 0 iota 设置为 Invalid,因此像 var f Face; f.String() 这样的语句不会返回有效数据,很明显该变量不是使用iota 常数。
    • 0 被称为InvalidNone 的区别在于语义和个人喜好;如果没有值是无效的,那么语义差异就会消失(无==无效),这纯粹是个人喜好。
    猜你喜欢
    • 2018-03-10
    • 2020-02-17
    • 2023-03-15
    • 1970-01-01
    • 2017-07-18
    • 1970-01-01
    • 2020-03-16
    • 1970-01-01
    • 2010-10-29
    相关资源
    最近更新 更多