2013-05-05 89 views
14

我目前工作的围棋郎教程,但是遇到了问题的一个练习:Go中的静态初始化?

https://tour.golang.org/methods/23

的行使,我实现一个ROT13加密。我决定使用从字节到其旋转值的映射来实现密码,但我不确定初始化此映射的最佳方式。我不想使用文字来初始化地图,但宁愿以编程方式通过循环字母表并在循环中设置(键,值)对来完成。我还希望地图只能从Rot13Reader结构/对象访问,并让所有实例(?)共享相同的地图(而不是每个Rot13Reader一个副本)。

这里是我当前的工作围棋程序:

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

这里有问题,我有这样的:

  • 我不希望有在main()
  • 我不准备rot13Map不希望rot13Map在全球范围内。
  • 我不想rot13Reader的每个副本有一个单独的rot13Map

有没有办法达到我想要进去吗?

+0

在一个有点相关的说明,为什么我要在'main'来定义我的嵌套函数为'无功的init = FUNC(...){...}',而不是'func init(...){...}'? (后者导致编译器错误) – jlhawn 2013-05-05 01:58:31

+0

我猜init不允许像main一样的参数。 – zk82 2013-05-06 06:32:21

+0

http://golang.org/ref/spec指定一个init函数(程序包级别的func init())不能从程序中的任何地方引用。 – zk82 2013-05-06 08:53:58

回答

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 
} 

很明显,您不能在巡回演出中制作另一个包。您被rot13Map卡住可以被main访问。您需要在本地运行Go以获得您想要的分离。

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! 
6

为完整起见:用于初始化的工作除了包中的init功能的有sync.Once,它运行一个提供的函数只有一次。

您创建一个Once对象,并使用其上的函数调用Do。只要对象的状态不变,提供的函数只会被调用一次。

例子:

import "sync" 

var readerInitOnce sync.Once 

func (rotr *rot13Reader) Read(p []byte) (int, error) { 
    readerInitOnce.Do(initRot13Map) 
    ... 
}