golang, dlaczego nie mamy określonej infrastruktury danych [zamknięte]

129

Próbuję rozwiązać ćwiczenie nr 1.4 „Język programowania go”, które wymaga posiadania zestawu. Mogę utworzyć typ zestawu, ale dlaczego nie ma takiego języka? idź, pochodząc z google, skąd również pochodzi guawa, dlaczego projektanci języka nie zdecydowali się na dodanie obsługi podstawowych struktur danych? po co zmuszać użytkowników do tworzenia własnych implementacji czegoś tak podstawowego jak zestaw?

anjanb
źródło
11
hmmm. zastanawiasz się, dlaczego głosy negatywne? Pochodzę ze świata Java, w którym mieliśmy zestaw prawie od samego początku, nawet bez generyków. Uważam więc to zachowanie za dziwaczne
anjanb

Odpowiedzi:

83

Częściowo dlatego, że Go nie ma typów ogólnych (więc potrzebujesz jednego typu zestawu dla każdego typu lub wrócić do refleksji, co jest raczej nieefektywne).

Częściowo dlatego, że jeśli wszystko czego potrzebujesz to "dodaj / usuńposzczególne elementy do zestawu" i "względnie oszczędzające miejsce", możesz uzyskać całkiem sporo tego po prostu używając map[yourtype]bool(i ustaw wartość truedla dowolnego elementu w zestawie ) lub, aby uzyskać większą oszczędność miejsca, możesz użyć pustej struktury jako wartości i użyć _, present = the_setoid[key]do sprawdzenia obecności.

Watyna
źródło
1
Ponadto wydaje się, że w duchu Go leży po prostu napisanie tego w swoim własnym kodzie, albo przez „wstawienie” zestawu w inny kod, albo zdefiniowanie własnego typu zestawu w razie potrzeby. W każdym razie użycie na przykład std :: set <T> w C ++ zawsze ma miejsce jako część implementacji funkcji lub implementacji innej struktury danych. Dlatego po prostu zaimplementuj tę inną strukturę danych bezpośrednio, używając map i wycinków oraz wszelkich innych potrzebnych bloków konstrukcyjnych, ale bez żadnego wbudowanego zestawu. Każde użycie zestawu będzie i tak używać go nieco inaczej :)
Bjarke Ebert,
1
Ale zwykle nie potrzebujesz samego zestawu <Foo>, używasz zestawu <Foo> jako części implementacji czegoś większego. Widziałem mnóstwo kodu, w którym czynności, które trzeba zrobić, aby dołączyć komponent „wielokrotnego użytku”, są znacznie gorsze niż tylko unikanie potrzeby. Więc mamy tutaj zestaw <Foo>, ta funkcja potrzebuje zestawu <Bar>, oops, czy mamy już kowariancję, albo co powiesz na zrobienie WrapperFactory, żeby ta rzecz wyglądała tak, itp. Może ta inna funkcja naprawdę potrzebuje tylko interfejsu, który może sprawdzić członkostwo, więc nie wysyłaj mu zestawu <Foo>.
Bjarke Ebert
42
Więc jeśli nie ma typów ogólnych, w jaki sposób w ogóle jest implementowana mapa „ogólna”?
Fermin Silva
26
Zauważ, że jeśli chcesz zaoszczędzić bajty, możesz użyć map[T]struct{}zamiast map[T]bool.
emersja
4
Zajrzyj na emersion.fr/blog/2017/sets-in-go
emersion
69

Jednym z powodów jest to, że łatwo jest utworzyć zestaw z mapy:

s := map[int]bool{5: true, 2: true}
_, ok := s[6] // check for existence
s[8] = true // add element 
delete(s, 2) // remove element

Unia

s_union := map[int]bool{}
for k, _ := range s1{
    s_union[k] = true
}
for k, _ := range s2{
    s_union[k] = true
}

Skrzyżowanie

s_intersection := map[int]bool{}
for k,_ := range s1 { 
  if s2[k] {
    s_intersection[k] = true
  }
}

Implementacja wszystkich innych operacji na zbiorach nie jest naprawdę trudna.

Salvador Dali
źródło
10
Sprawdzanie istnienia to po prostu indeksowanie mapy. Ponieważ jeśli go nie ma, wartość zerowa (czyli false) właściwie to powie. Do testowania nie jest potrzebny idiom z przecinkiem ok.
icza
2
Implementacja dla przecięcia wygląda tak dla różnicy.
musiphil
1
@Kittsil dziękuję. Zaktualizowano.
Salvador Dali
34
Jest to bardziej optymalne w użyciu map[int]struct{}zamiast bool, ponieważ pusta struktura zajmuje 0 bajtów w pamięci. Niedawno napisałem streszczenie
BG Adrian
13
To nie jest takie proste. Samo pisanie tego kodu wszędzie tam, gdzie potrzebujesz zestawu, wydaje mi się obecnie śmieszne. Wsparcie dla kolekcji powinno być zapewnione w dowolnym języku. Pomyśl, że lepszą odpowiedzią jest to, że Go nie jest jeszcze tak dojrzałe. Jestem pewien, że wkrótce pojawią się biblioteki, które to omówią.
Stef
5

Jak napisał Vatine: Ponieważ w go brakuje typów generycznych, musiałby on być częścią języka, a nie standardową biblioteką. W tym celu musiałbyś wtedy zaśmiecić język za pomocą zestawu słów kluczowych, unii, przecięcia, różnicy, podzbioru ...

Innym powodem jest to, że w ogóle nie jest jasne, jaka jest „właściwa” implementacja zestawu:

  1. Istnieje podejście funkcjonalne:

    func IsInEvenNumbers(n int) bool {
        if n % 2 == 0 {
            return true
        }
       return false
    }
    

To jest zbiór wszystkich równych int. Ma bardzo wydajne wyszukiwanie, a sumę, przecięcie, różnicę i podzbiór można łatwo wykonać za pomocą kompozycji funkcjonalnej.

  1. Albo masz podejście podobne do tego, które pokazał Dali.

Mapa nie ma tego problemu, ponieważ przechowujesz coś związanego z wartością.

0x434D53
źródło
2
Aby obsłużyć zestawy wbudowane, Pascal przeciąża kilka operatorów binarnych (dwuargumentowych): +dla unii, -dla różnicy, *dla przecięcia, <=dla podzbioru, >=dla superzbioru, =dla równości, <>dla nierówności i indla członkostwa. Tak więc w Go będzie to tylko jedno nowe słowo kluczowe - in. Z drugiej strony, wbudowane zestawy Pascala działają tylko na „liczbach porządkowych” - to znaczy na każdym typie, który ma podstawową reprezentację wartości całkowitej o pewnym rozmiarze.
kostix
9
Fakt, że istnieje wiele sposobów implementacji zestawu, nie powstrzymał wielu innych języków przed ich udostępnieniem.
augurar
@kostix: Go może nawet użyć składni s[key](tak jakby sbyła a map[T]bool) zamiast key in s.
musiphil
2
Jest jakiś powód, by po prostu nie wrócić n % 2 == 0?
João Andrade
4

Inną możliwością jest użycie zestawów bitów, dla których istnieje przynajmniej jeden pakiet lub można skorzystać z wbudowanego dużego pakietu. W tym przypadku zasadniczo musisz zdefiniować sposób konwersji obiektu na indeks.

Will Fitzgerald
źródło
1
Powinienem zauważyć, że napisałem oryginalną wersję pakietu bitset, o którym mowa powyżej.
Will Fitzgerald