2013-05-05 7 views
14

Я сейчас работаю на учебнике Go Lang, но столкнулся с проблемой с одним из упражнений:Статическая инициализация в Go?

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()
  • Я не 't хотите, чтобы rot13Map находился в глобальном масштабе.
  • Я не хочу, каждая копия rot13Reader иметь отдельную rot13Map

Есть ли способ добиться того, чего я хочу в Go?

+0

На несколько связанной ноте, почему я должен определить свою вложенную функцию в 'main' как' вар Инициализационные = FUNC (...) {...} 'и не 'func init (...) {...}'? (последний вызывает ошибку компилятора) – jlhawn

+0

Я думаю, что init не позволяет использовать параметры, такие как main. – zk82

+0

http://golang.org/ref/spec указывает, что функция init (func init() на уровне пакета) не может упоминаться повсюду в программе. – zk82

ответ

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 доступен по умолчанию. Вам нужно будет запустить 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 с вашей функцией на нем. Пока состояние объекта Once не изменяется, функция, которую вы подаете, будет вызываться только один раз.

Пример:

import "sync" 

var readerInitOnce sync.Once 

func (rotr *rot13Reader) Read(p []byte) (int, error) { 
    readerInitOnce.Do(initRot13Map) 
    ... 
} 
Смежные вопросы