英文:
Static Initialization in Go?
问题
我目前正在学习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]; 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。
在Go中有办法实现我想要的吗?
英文:
I'm currently working on the Go Lang tutorial, but ran into problem with one of the exercises:
https://tour.golang.org/methods/23
The exercise has me implement a ROT13 cipher. I decided to implement the cipher using a map from a byte to its rotated value but I'm not sure of the best way to initialize this map. I don't want to initialize the map using a literal, but would prefer to do it programmatically by looping through an alphabet and setting (key, value) pairs within the loop. I would also like the map to only be accessible from Rot13Reader struct/object and have all instances(?) share the same map (rather than one copy per Rot13Reader).
Here's my current working Go program:
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]; 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)
}
Here are the problems I have with this:
- I don't want to have to prepare <code>rot13Map</code> in <code>main()</code>
- I don't want <code>rot13Map</code> to be in global scope.
- I don't want each copy of a <code>rot13Reader</code> to have a separate <code>rot13Map</code>
Is there a way to achieve what I want in Go?
答案1
得分: 16
为了做到这一点,我会创建一个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教程中无法创建另一个包。您只能通过main函数访问rot13Map。您需要在本地运行Go以获得所需的分离。
英文:
In order to do this, I would make a rot13 package. You can programmatically create the map in an init() function and provide it as a package level global to all your rot13 decoders. The init function runs when your package is imported.
Because Rot13Reader is the only type in the package, it is the only one able to access your map.
WARNING: All code untested.
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]; sub != byte(0) {
p[i] = sub
}
}
return n, err
}
Obviously, you can't make another package in the go tour. You are stuck with rot13Map being accessible by main. You will need to run Go locally to get the separation you want.
答案2
得分: 6
为了完整性起见:除了包中的init
函数之外,还有一个sync.Once
用于初始化工作,它只运行一次提供的函数。
您可以创建一个Once
对象,并在其上调用Do
函数以执行您的函数。只要Once
对象的状态没有改变,提供的函数将只被调用一次。
示例:
import "sync"
var readerInitOnce sync.Once
func (rotr *rot13Reader) Read(p []byte) (int, error) {
readerInitOnce.Do(initRot13Map)
...
}
英文:
For the sake of completeness: For initialization work besides of the init
function in a package there is sync.Once
, which runs a supplied function only once.
You create an Once object and call Do
with your function on it. As long as the state of the Once
object is not changed, the supplied function will only be called once.
Example:
import "sync"
var readerInitOnce sync.Once
func (rotr *rot13Reader) Read(p []byte) (int, error) {
readerInitOnce.Do(initRot13Map)
...
}
答案3
得分: 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!
英文:
I would simplify your code and use an init
function. For example,
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)
}
Output:
You cracked the code!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论