2013-05-05 19 views
14

che sto attualmente lavorando il tutorial Go Lang, ma ha incontrato un problema con uno degli esercizi:Inizializzazione statica in Go?

https://tour.golang.org/methods/23

L'esercizio mi ha implementare un cifrario ROT13. Ho deciso di implementare il codice utilizzando una mappa da un byte al suo valore ruotato, ma non sono sicuro del modo migliore per inizializzare questa mappa. Non voglio inizializzare la mappa usando un letterale, ma preferirei farlo a livello di programmazione passando da un alfabeto e impostando coppie (chiave, valore) all'interno del ciclo. Mi piacerebbe anche che la mappa fosse accessibile solo dalla struttura/oggetto Rot13Reader e che tutte le istanze (?) Condividessero la stessa mappa (piuttosto che una copia per Rot13Reader).

Ecco il mio attuale lavoro programma di 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) 
} 

Qui sono i problemi che ho con questo:

  • Io non voglio avere a preparare rot13Map in main()
  • I don Non voglio che rot13Map sia nello scope globale.
  • non voglio ogni copia di un rot13Reader di avere un separato rot13Map

C'è un modo per ottenere quello che voglio in Go?

+0

In una nota correlata in qualche modo, perché devo definire la mia funzione annidata in 'main' come' var init = func (...) {...} 'e non 'func init (...) {...}'? (quest'ultimo causa un errore del compilatore) – jlhawn

+0

Suppongo che init non permetta parametri, come main. – zk82

+0

http://golang.org/ref/spec specifica che una funzione di inizializzazione (func init() a livello di pacchetto) non può essere indirizzata da ovunque in un programma. – zk82

risposta

11

Per fare questo, vorrei creare un pacchetto rot13. È possibile creare la mappa in modo programmatico in una funzione init() e fornirla come un livello di pacchetto globale per tutti i decodificatori rot13. La funzione init viene eseguita quando il pacchetto viene importato.

Poiché Rot13Reader è l'unico tipo nel pacchetto, è l'unico in grado di accedere alla mappa.

ATTENZIONE: tutti i codici non testati.

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 
} 

Ovviamente, non è possibile creare un altro pacchetto durante il tour. Sei bloccato con rot13Map accessibile da main. Dovrai eseguire Go localmente per ottenere la separazione desiderata.

4

Vorrei semplificare il codice e utilizzare la funzione init. Ad esempio,

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) 
} 

uscita:

You cracked the code! 
6

per ragioni di completezza: per lavoro di inizializzazione oltre la funzione init in un pacchetto c'è sync.Once, che esegue una funzione fornita solo una volta.

Si crea un oggetto Once e si chiama Do con la funzione su di esso. Finché lo stato dell'oggetto non viene modificato, la funzione fornita verrà chiamata una sola volta.

Esempio:

import "sync" 

var readerInitOnce sync.Once 

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