我目前正在研究Go Lang教程,但其中一个练习遇到了问题:
https://tour.golang.org/methods/23
练习让我实现了一个ROT13密码。我决定使用从字节到旋转值的映射来实现密码,但我不确定初始化此映射的最佳方法。我不想使用文字初始化地图,但更喜欢通过循环遍历字母表并在循环内设置(键,值)对来编程。我还希望只能从Rot13Reader结构/对象访问该地图,并让所有实例(?)共享相同的地图(而不是每个Rot13Reader一个副本)。
这是我目前正在进行的Go计划:
package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
var rot13Map = map[byte]byte{}
func (rotr *rot13Reader) Read(p []byte) (int, error) {
n, err := rotr.r.Read(p)
for i := 0; i < n; i++ {
if sub := rot13Map[p[i]]; sub != byte(0) {
p[i] = sub
}
}
return n, err
}
func main() {
func() {
var uppers = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
var lowers = []byte("abcdefghijklmnopqrstuvwxyz")
var init = func (alphabet []byte) {
for i, char := range alphabet {
rot13_i := (i + 13) % 26
rot13Map[char] = alphabet[rot13_i]
}
}
init(uppers)
init(lowers)
}()
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
以下是我遇到的问题:
rot13Map
main()
rot13Map
在全球范围内。rot13Reader
的每个副本都有一个单独的rot13Map
有没有办法在Go中实现我想要的东西?
答案 0 :(得分:11)
为了做到这一点,我会做一个rot13包。您可以在init()函数中以编程方式创建映射,并将其作为包级别全局提供给所有rot13解码器。导入包时会运行init函数。
因为Rot13Reader是包中唯一的类型,所以它是唯一能够访问地图的类型。
警告:所有代码均未经测试。
package rot13
import (
"io"
)
var rot13Map = map[byte]byte{}
func init() {
var uppers = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
var lowers = []byte("abcdefghijklmnopqrstuvwxyz")
var init = func(alphabet []byte) {
for i, char := range alphabet {
rot13_i := (i + 13) % 26
rot13Map[char] = alphabet[rot13_i]
}
}
init(uppers)
init(lowers)
}
type Reader struct {
r io.Reader
}
func (rotr Reader) Read(p []byte) (int, error) {
n, err := rotr.r.Read(p)
for i := 0; i < n; i++ {
if sub := rot13Map[p[i]]; sub != byte(0) {
p[i] = sub
}
}
return n, err
}
显然,你不能在游览中制作另一个包。您可以通过main访问rot13Map。您需要在本地运行Go才能获得所需的分离。
答案 1 :(得分:6)
为了完整起见:除了包中的init
函数之外的初始化工作还有sync.Once
,它只运行一次提供的函数。
您创建一个Once对象并使用您的函数调用Do
。只要Once
的状态
对象没有改变,提供的函数只会被调用一次。
示例:
import "sync"
var readerInitOnce sync.Once
func (rotr *rot13Reader) Read(p []byte) (int, error) {
readerInitOnce.Do(initRot13Map)
...
}
答案 2 :(得分:4)
我会简化您的代码并使用init
函数。例如,
package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
func newRot13Map() map[byte]byte {
n := byte('Z' - 'A' + 1)
rot13 := make(map[byte]byte, 2*n)
for ltr := byte(0); ltr < n; ltr++ {
sub := (ltr + 13) % n
rot13[ltr+'A'] = sub + 'A'
rot13[ltr+'a'] = sub + 'a'
}
return rot13
}
var rot13Map map[byte]byte
func init() {
rot13Map = newRot13Map()
}
func (rotr *rot13Reader) Read(p []byte) (int, error) {
n, err := rotr.r.Read(p)
for i, ltr := range p[:n] {
if sub, ok := rot13Map[ltr]; ok {
p[i] = sub
}
}
return n, err
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
输出:
You cracked the code!