Мені дуже подобається google golang, але чи міг би хтось пояснити, яке обґрунтування полягає у тому, щоб реалізатори залишили базову структуру даних, таку як набори зі стандартної бібліотеки?
Мені дуже подобається google golang, але чи міг би хтось пояснити, яке обґрунтування полягає у тому, щоб реалізатори залишили базову структуру даних, таку як набори зі стандартної бібліотеки?
Відповіді:
Однією з потенційних причин цього упущення є те, що моделювати набори з картою дуже просто.
Якщо чесно, я думаю, що це теж трохи недогляд, але, дивлячись на Perl, історія точно така ж. У Perl ви отримуєте списки та хештелі, у Go ви отримуєте масиви, фрагменти та карти. У Perl ви, як правило, використовуєте хештел для будь-яких проблем, пов’язаних із набором, те саме стосується Go.
Приклад
щоб наслідувати набір ints в Go, визначимо карту:
set := make(map[int]bool)
Додавати щось так просто, як:
i := valueToAdd()
set[i] = true
Видалити щось просто
delete(set, i)
І потенційна незграбність цієї конструкції легко усунути:
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
}
І видалити і отримати Аналогічно можна визначити, у мене є повна реалізація тут . Основним розладом тут є той факт, що у Go не є дженерики. Однак це можливо зробити interface{}
в тому випадку, коли б ви привели результати отримання.
map[int]bool
можна використовувати один map[int]struct{}
. Я віддаю перевагу останньому.
map[int]struct{}
.. struct{}
Займає 0 байт.
map[int]struct{}
ви не можете зробити if mymap["key"] {
перевірку на членство. Google рекомендує використовуватиbool
(пошук "Набір може бути реалізований").
Я думаю, що це стосується golang
зосередженості на простоті. set
и стати дуже корисним з difference
, intersection
, union
, issubset
і так далі .. методи. Можливо, golang
команда відчула, що це занадто багато для однієї структури даних. Але в іншому випадку "тупий набір", який є лише add
, contains
і remove
його можна легко повторити, map
як пояснив @jozefg.
Попередня відповідь працює ТІЛЬКО, якщо ключ є вбудованим типом. Щоб доповнити попередню відповідь, ось спосіб реалізувати набір, елементи якого визначені користувачем типів:
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
працює прекрасно. Все, що потрібно для типу ключа, який використовується на карті, - це те, що він має ==
і!=
. Рівність типів структур чітко визначена, ваш Equals
метод повинен бути справедливим p1 == p2
.
Contains
займає лінійний час, а aMap[]
постійний час, незалежно від кількості членів. Кращим рішенням буде внутрішньо створити унікальний ключ, що базується на вмісті кожного члена, та використовувати запити постійного часу, які map
надає тип. Існують навіть більш швидкі рішення, що враховують поведінку кешу тощо.