Eu realmente gosto do google golang, mas alguém poderia explicar qual é a lógica para os implementadores terem deixado de fora uma estrutura de dados básica, como conjuntos da biblioteca padrão?
Eu realmente gosto do google golang, mas alguém poderia explicar qual é a lógica para os implementadores terem deixado de fora uma estrutura de dados básica, como conjuntos da biblioteca padrão?
Respostas:
Um motivo potencial para essa omissão é que é realmente fácil modelar conjuntos com um mapa.
Para ser sincero, acho que também é um pouco de supervisão, no entanto, olhando para Perl, a história é exatamente a mesma. No Perl, você obtém listas e hashtables, no Go, obtém matrizes, fatias e mapas. No Perl, você geralmente usa uma hashtable para todo e qualquer problema relacionado a um conjunto, o mesmo se aplica ao Go.
Exemplo
para imitar um conjunto de entradas no Go, definimos um mapa:
set := make(map[int]bool)
Adicionar algo é tão fácil quanto:
i := valueToAdd()
set[i] = true
Excluir algo é apenas
delete(set, i)
E o constrangimento potencial dessa construção é facilmente abstraído:
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 delete e get podem ser definidos da mesma forma, eu tenho a implementação completa aqui . A principal desvantagem aqui é o fato de que go não possui genéricos. No entanto, é possível fazer isso, interface{}
caso em que você lançaria os resultados de get.
map[int]bool
um pode usar em seu map[int]struct{}
lugar. Eu prefiro o último.
map[int]struct{}
.. O struct{}
leva 0 bytes.
map[int]struct{}
você não pode fazer if mymap["key"] {
para verificar a adesão. O Google recomenda o usobool
(pesquise "Um conjunto pode ser implementado").
Eu acho que isso tem a ver com o golang
foco na simplicidade. set
s se tornar realmente útil com difference
, intersection
, union
, issubset
, e assim por diante .. métodos. Talvez a golang
equipe tenha achado que é demais para uma estrutura de dados. Mas caso contrário, um "conjunto de burro" que só tem add
, contains
e remove
pode ser facilmente replicado com map
como explicado por @jozefg.
A resposta anterior funciona apenas se a chave for do tipo interno. Para complementar a resposta anterior, aqui está uma maneira de implementar um conjunto cujos elementos são tipos definidos pelo usuário:
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 perfeitamente bem. Tudo o que é necessário para o tipo de chave usado em um mapa é que ele possui ==
e!=
. A igualdade dos tipos de estrutura está bem definida, seu Equals
método deve ser justo p1 == p2
.
Contains
leva tempo linear, enquanto aMap[]
leva tempo constante, independentemente do número de membros. Uma solução melhor criaria internamente uma chave exclusiva com base no conteúdo de cada membro e alavancaria a consulta em tempo constante que o map
tipo fornece. Soluções ainda mais rápidas que consideram o comportamento do cache etc. também existem.