Realmente me gusta Google Golang, pero ¿alguien podría explicar cuál es la razón para que los implementadores hayan omitido una estructura de datos básica como los conjuntos de la biblioteca estándar?
Realmente me gusta Google Golang, pero ¿alguien podría explicar cuál es la razón para que los implementadores hayan omitido una estructura de datos básica como los conjuntos de la biblioteca estándar?
Respuestas:
Una razón potencial para esta omisión es que es realmente fácil modelar conjuntos con un mapa.
Para ser honesto, creo que también es un poco descuidado, sin embargo, mirando a Perl, la historia es exactamente la misma. En Perl obtienes listas y tablas hash, en Go obtienes matrices, sectores y mapas. En Perl, generalmente usaría una tabla hash para todos y cada uno de los problemas relacionados con un conjunto, lo mismo se aplica a Go.
Ejemplo
Para imitar un conjunto de entradas en Go, definimos un mapa:
set := make(map[int]bool)
Agregar algo es tan fácil como:
i := valueToAdd()
set[i] = true
Eliminar algo es solo
delete(set, i)
Y la incomodidad potencial de esta construcción se abstrae fácilmente:
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
}
Y eliminar y obtener se puede definir de manera similar, tengo la implementación completa aquí . La principal desventaja aquí es el hecho de que ir no tiene genéricos. Sin embargo, es posible hacer esto, interface{}
en cuyo caso habría emitido los resultados de get.
map[int]bool
uno puede usar map[int]struct{}
en su lugar. Prefiero el último
map[int]struct{}
.. El struct{}
toma 0 bytes.
map[int]struct{}
usted no puede hacer if mymap["key"] {
para verificar la membresía. Google recomienda usarbool
(busque "Se puede implementar un conjunto").
Creo que esto tiene que ver con el golang
enfoque en la simplicidad. set
s llegar a ser realmente útil con difference
, intersection
, union
, issubset
, y así sucesivamente .. métodos. Quizás el golang
equipo consideró que es demasiado para una estructura de datos. Pero por lo demás, es un "conjunto tonto" que solo tiene add
, contains
y remove
puede ser fácilmente replicado con map
lo explicado por @jozefg.
La respuesta anterior funciona SOLO SI la clave es un tipo incorporado. Para complementar la respuesta anterior, aquí hay una manera de implementar un conjunto cuyos elementos son tipos definidos por el usuario:
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
Funciona perfectamente bien. Todo lo que se requiere del tipo de clave utilizado en un mapa es que tiene ==
y!=
. La igualdad de los tipos de estructura está bien definida, su Equals
método debe ser justo p1 == p2
.
Contains
toma tiempo lineal, mientras que aMap[]
toma tiempo constante, independientemente del número de miembros. Una solución mejor crearía internamente una clave única basada en el contenido de cada miembro y aprovecharía las consultas de tiempo constante que map
proporciona el tipo. Incluso existen soluciones más rápidas que consideran el comportamiento de la caché, etc.