Mi piace molto Google Golang, ma qualcuno potrebbe spiegare qual è la logica per gli implementatori che hanno lasciato fuori una struttura di dati di base come set dalla libreria standard?
Mi piace molto Google Golang, ma qualcuno potrebbe spiegare qual è la logica per gli implementatori che hanno lasciato fuori una struttura di dati di base come set dalla libreria standard?
Risposte:
Una potenziale ragione di questa omissione è che è davvero facile modellare i set con una mappa.
Ad essere sincero, penso che sia anche un po 'una svista, tuttavia guardando Perl, la storia è esattamente la stessa. In Perl ottieni elenchi e hashtabili, in Vai ottieni matrici, sezioni e mappe. In Perl useresti generalmente una tabella hash per tutti i problemi relativi a un set, lo stesso vale per Go.
Esempio
per imitare un set in Go, definiamo una mappa:
set := make(map[int]bool)
Aggiungere qualcosa è facile come:
i := valueToAdd()
set[i] = true
Eliminare qualcosa è giusto
delete(set, i)
E il potenziale imbarazzo di questo costrutto viene facilmente sottratto:
type IntSet struct {
set map[int]bool
}
func (set *IntSet) Add(i int) bool {
_, found := set.set[i]
set.set[i] = true
return !found //False if it existed already
}
E cancellare e ottenere può essere definito in modo simile, ho l'implementazione completa qui . Lo svantaggio principale qui è il fatto che andare non ha generici. Tuttavia è possibile farlo con interface{}nel qual caso avresti lanciato i risultati di get.
map[int]booluno può usare map[int]struct{}invece. Preferisco l'ultimo.
map[int]struct{}.. Il struct{}prende 0 byte.
map[int]struct{}te non puoi fare if mymap["key"] {per verificare l'appartenenza. Google consiglia di utilizzarebool (cercare "È possibile implementare un set").
Penso che questo abbia a che fare con l' golangattenzione sulla semplicità. sets diventare veramente utile con difference, intersection, union, issubset, e così via .. metodi. Forse il golangteam ha ritenuto che fosse troppo per una struttura di dati. Ma altrimenti un "set dumb" che ha solo add, containse removepuò essere facilmente replicato con mapcome spiegato da @jozefg.
La risposta precedente funziona SOLO SE la chiave è di tipo incorporato. Per completare la risposta precedente, ecco un modo per implementare un set i cui elementi sono tipi definiti dall'utente:
package math
// types
type IntPoint struct {
X, Y int
}
// set implementation for small number of items
type IntPointSet struct {
slice []IntPoint
}
// functions
func (p1 IntPoint) Equals(p2 IntPoint) bool {
return (p1.X == p2.X) && (p1.Y == p2.Y)
}
func (set *IntPointSet) Add(p IntPoint) {
if ! set.Contains(p) {
set.slice = append(set.slice, p)
}
}
func (set IntPointSet) Contains(p IntPoint) bool {
for _, v := range set.slice {
if v.Equals(p) {
return true
}
}
return false
}
func (set IntPointSet) NumElements() int {
return len(set.slice)
}
func NewIntPointSet() IntPointSet {
return IntPointSet{(make([]IntPoint, 0, 10))}
}
type mySet map[IntPoint]boolfunziona perfettamente. Tutto ciò che è richiesto per il tipo di chiave utilizzato in una mappa è che ha ==e!= . L'uguaglianza dei tipi di struttura è ben definita, il Equalsmetodo dovrebbe essere giusto p1 == p2.
Containsrichiede tempo lineare, mentre aMap[]richiede tempo costante, indipendentemente dal numero di membri. Una soluzione migliore creerebbe internamente una chiave univoca basata sul contenuto di ciascun membro e sfrutterà le query a tempo costante mapfornite dal tipo. Esistono anche soluzioni più veloci che considerano il comportamento della cache, ecc.