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[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)
}
Here are the problems I have with this:
rot13Map
in main()
rot13Map
to be in global scope.rot13Reader
to have a separate rot13Map
Is there a way to achieve what I want in 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[p[i]]; 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.