用 iota 枚举字符串常量
Enumerating string constants with iota
以下示例使用 iota 定义了一系列从 3333 开始的端口号。
package main
import (
"fmt"
)
const (
FirstPort = iota+3333
SecondPort
ThirdPort
)
func main() {
hostAndPort := "localhost:"+fmt.Sprint(SecondPort)
fmt.Printf("%s", hostAndPort )
// Output:
// localhost:3334
}
组合主机名和端口时,我想避免将端口常量包装在 fmt.Sprint
中,而只需编写 "localhost:"+SecondPort
。有没有办法使用 iota 将端口号定义为字符串常量,例如 "3334"
?
以下无效:
FirstPort = string(iota + 3333)
也没有
FirstPort = fmt.Sprintf("%d", iota + 3333)
您正在创建无类型数字常量。如有疑问,请检查 the spec。要创建一个包含主机和端口号的字符串,您可以简单地使用 fmt.Sprintf
,如下所示:
package main
const (
FirstPort = iota+3333
SecondPort
ThirdPort
)
func main() {
hostPort := fmt.Sprintf("localhost:%d", FirstPort)
fmt.Println(hostPort)
}
仅此而已:Demo
引用自Spec: Iota:
Within a constant declaration, the predeclared identifier iota
represents successive untyped integer constants.
因此 iota
为您提供整数常量。如果我们想要 string
常量,我们需要找到一种方法将整数转换为其以 10 为底的 string
表示形式。这种方式必须是constant expression,否则我们不能在常量声明中使用它。
对我们来说不幸的是,从整数到 string
的简单类型 conversion 不会产生数值的以 10 为底的表示形式,但是:
Converting a signed or unsigned integer value to a string type yields a string containing the UTF-8 representation of the integer.
所以结果将是 string
持有一个符文,其值(Unicode 代码点)是源编号。
也不可能调用 "converter" 函数,例如 strconv.Itoa()
or fmt.Sprint()
,因为调用这些函数不能成为常量表达式的一部分,因此结果只能用于变量声明 (更不用说我们不能使用 iota
,它只允许在常量声明中使用。
不过还是有办法的
我认为不值得麻烦和失去可读性,但实际上你可以定义 string
常量 使用 [=18 来保存递增的十进制数=].
该解决方案从数字构建 "complete" 数字。我们可以通过连接数字的数字(作为 string
值)来获得以 10 string
表示。
要解决的最后一个问题是如何 "list" 数字的数字。这是简单的算术:
- 数字的最后一位(以 10 为基数)是
i % 10
。
- 前面的数字是
i / 10 % 10
.
- 前一个是
i / 100 % 10
。
- 等等...
而要获得一个数字(在0..9
范围内)的rune
,我们可以简单地向其添加'0'
,并将其转换为string
.仅此而已。
这就是我们如何将其编码为 1 位字符串数字:
n0 = string('0'+iota%10)
对于 2 位数号码:
n00 = string('0'+iota/10%10) + string('0'+iota/1%10)
对于 3 位数号码:
n000 = string('0'+iota/100%10) + string('0'+iota/10%10) + string('0'+iota/1%10)
让我们看看实际效果:
const (
P00 = string('0'+iota/10%10) + string('0'+iota/1%10)
P01
P02
P03
P04
P05
P06
P07
P08
P09
P10
P11
P12
P13
P14
P15
P16
P17
P18
P19
P20
)
正在打印结果:
fmt.Printf("%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n",
P00, P01, P02, P03, P04, P05, P06, P07, P08, P09,
P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20)
输出(在Go Playground上试试):
00
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
到目前为止一切顺利,但我们如何让它从 3333
开始?
也不是问题,很容易就能实现。我们可以 移动 iota,只需向其添加 "initial" 数字即可。这就是全部。
让我们看一个例子,其中第一个数字是 3339
:
const (
P3339 = string('0'+(iota+3339)/1000%10) +
string('0'+(iota+3339)/100%10) +
string('0'+(iota+3339)/10%10) +
string('0'+(iota+3339)/1%10)
P3340
P3341
)
func main() {
fmt.Println(P3339)
fmt.Println(P3340)
fmt.Println(P3341)
}
上面的输出是预期的(在 Go Playground 上尝试):
3339
3340
3341
以下示例使用 iota 定义了一系列从 3333 开始的端口号。
package main
import (
"fmt"
)
const (
FirstPort = iota+3333
SecondPort
ThirdPort
)
func main() {
hostAndPort := "localhost:"+fmt.Sprint(SecondPort)
fmt.Printf("%s", hostAndPort )
// Output:
// localhost:3334
}
组合主机名和端口时,我想避免将端口常量包装在 fmt.Sprint
中,而只需编写 "localhost:"+SecondPort
。有没有办法使用 iota 将端口号定义为字符串常量,例如 "3334"
?
以下无效:
FirstPort = string(iota + 3333)
也没有
FirstPort = fmt.Sprintf("%d", iota + 3333)
您正在创建无类型数字常量。如有疑问,请检查 the spec。要创建一个包含主机和端口号的字符串,您可以简单地使用 fmt.Sprintf
,如下所示:
package main
const (
FirstPort = iota+3333
SecondPort
ThirdPort
)
func main() {
hostPort := fmt.Sprintf("localhost:%d", FirstPort)
fmt.Println(hostPort)
}
仅此而已:Demo
引用自Spec: Iota:
Within a constant declaration, the predeclared identifier
iota
represents successive untyped integer constants.
因此 iota
为您提供整数常量。如果我们想要 string
常量,我们需要找到一种方法将整数转换为其以 10 为底的 string
表示形式。这种方式必须是constant expression,否则我们不能在常量声明中使用它。
对我们来说不幸的是,从整数到 string
的简单类型 conversion 不会产生数值的以 10 为底的表示形式,但是:
Converting a signed or unsigned integer value to a string type yields a string containing the UTF-8 representation of the integer.
所以结果将是 string
持有一个符文,其值(Unicode 代码点)是源编号。
也不可能调用 "converter" 函数,例如 strconv.Itoa()
or fmt.Sprint()
,因为调用这些函数不能成为常量表达式的一部分,因此结果只能用于变量声明 (更不用说我们不能使用 iota
,它只允许在常量声明中使用。
不过还是有办法的
我认为不值得麻烦和失去可读性,但实际上你可以定义 string
常量 使用 [=18 来保存递增的十进制数=].
该解决方案从数字构建 "complete" 数字。我们可以通过连接数字的数字(作为 string
值)来获得以 10 string
表示。
要解决的最后一个问题是如何 "list" 数字的数字。这是简单的算术:
- 数字的最后一位(以 10 为基数)是
i % 10
。 - 前面的数字是
i / 10 % 10
. - 前一个是
i / 100 % 10
。 - 等等...
而要获得一个数字(在0..9
范围内)的rune
,我们可以简单地向其添加'0'
,并将其转换为string
.仅此而已。
这就是我们如何将其编码为 1 位字符串数字:
n0 = string('0'+iota%10)
对于 2 位数号码:
n00 = string('0'+iota/10%10) + string('0'+iota/1%10)
对于 3 位数号码:
n000 = string('0'+iota/100%10) + string('0'+iota/10%10) + string('0'+iota/1%10)
让我们看看实际效果:
const (
P00 = string('0'+iota/10%10) + string('0'+iota/1%10)
P01
P02
P03
P04
P05
P06
P07
P08
P09
P10
P11
P12
P13
P14
P15
P16
P17
P18
P19
P20
)
正在打印结果:
fmt.Printf("%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n",
P00, P01, P02, P03, P04, P05, P06, P07, P08, P09,
P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20)
输出(在Go Playground上试试):
00
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
到目前为止一切顺利,但我们如何让它从 3333
开始?
也不是问题,很容易就能实现。我们可以 移动 iota,只需向其添加 "initial" 数字即可。这就是全部。
让我们看一个例子,其中第一个数字是 3339
:
const (
P3339 = string('0'+(iota+3339)/1000%10) +
string('0'+(iota+3339)/100%10) +
string('0'+(iota+3339)/10%10) +
string('0'+(iota+3339)/1%10)
P3340
P3341
)
func main() {
fmt.Println(P3339)
fmt.Println(P3340)
fmt.Println(P3341)
}
上面的输出是预期的(在 Go Playground 上尝试):
3339
3340
3341