Мені дуже подобається 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надає тип. Існують навіть більш швидкі рішення, що враховують поведінку кешу тощо.