2013-05-05 85 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) 
    ... 
}