Статическая инициализация в Go? - программирование
Подтвердить что ты не робот

Статическая инициализация в Go?

В настоящее время я работаю над учебником 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?

4b9b3361

Ответ 1

Чтобы сделать это, я сделал бы пакет 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 локально, чтобы получить желаемое разделение.

Ответ 2

Для полноты: для работы с инициализацией помимо функции init в пакете есть sync.Once, который запускает только один раз.

Вы создаете объект Once и вызываете Do с помощью своей функции. Пока состояние Once объект не будет изменен, функция, которая будет отправлена, будет вызываться только один раз.

Пример:

import "sync"

var readerInitOnce sync.Once

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

Ответ 3

Я бы упростил ваш код и использовал функцию 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!