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]bool
uno 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' golang
attenzione sulla semplicità. set
s diventare veramente utile con difference
, intersection
, union
, issubset
, e così via .. metodi. Forse il golang
team ha ritenuto che fosse troppo per una struttura di dati. Ma altrimenti un "set dumb" che ha solo add
, contains
e remove
può essere facilmente replicato con map
come 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]bool
funziona 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 Equals
metodo dovrebbe essere giusto p1 == p2
.
Contains
richiede 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 map
fornite dal tipo. Esistono anche soluzioni più veloci che considerano il comportamento della cache, ecc.