15

我目前正在编写 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在全球范围内。
  • 我不希望 a 的每个副本rot13Reader都有一个单独的rot13Map

有没有办法在 Go 中实现我想要的?

4

3 回答 3

15

为了做到这一点,我会制作一个 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
}

显然,您不能在 go tour 中制作另一个包裹。您被 main 访问的 rot13Map 卡住了。您将需要在本地运行 Go 以获得所需的分离。

于 2013-05-05T02:55:32.247 回答
6

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

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

例子:

import "sync"

var readerInitOnce sync.Once

func (rotr *rot13Reader) Read(p []byte) (int, error) {
    readerInitOnce.Do(initRot13Map)
    ...
}
于 2013-05-05T22:31:38.167 回答
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!
于 2013-05-05T05:48:28.450 回答