Zawiera metodę dla plasterka


Odpowiedzi:


226

Mostafa wskazał już, że taka metoda jest trywialna do napisania, a mkb dał ci wskazówkę, jak korzystać z wyszukiwania binarnego z pakietu sortowania. Ale jeśli zamierzasz wykonać wiele takich kontroli, możesz również rozważyć użycie mapy.

Sprawdzanie, czy istnieje określony klucz mapy, jest proste za pomocą value, ok := yourmap[key]idiomu. Ponieważ nie jesteś zainteresowany tą wartością, możesz również utworzyć map[string]struct{}na przykład. Korzystanie z pustego struct{}miejsca ma tę zaletę, że nie wymaga dodatkowej przestrzeni, a wewnętrzny typ mapy Go jest zoptymalizowany dla tego rodzaju wartości. Dlatego map[string] struct{}jest popularnym wyborem dla zestawów w świecie Go.


27
Pamiętaj również, że musisz napisać, struct{}{}aby uzyskać wartość pustej struktury, abyś mógł przekazać ją do mapy, gdy chcesz dodać element. Po prostu spróbuj, a jeśli napotkasz jakiekolwiek problemy, możesz zapytać. Możesz także skorzystać z rozwiązania Mostafa, jeśli łatwiej to zrozumieć (chyba że masz ogromne ilości danych).
tux21b

5
Rozwiązanie jest proste, to prawda. Ale co trzeba, aby dodać taką podstawową funkcjonalność do środowiska wykonawczego? Nie znalazłem takich problemów w repozytorium Go na github. To smutne i dziwne.
Igor Pietrow

1
Jak wypada map[string] boolporównanie map[string] struct{}. map[string] struct{}wygląda na włamanie szczególnie inicjujące pustą strukturęstruct {}{}
vadasambar

@IgorPetrov zgodził się, jestem zaskoczony, że tak podstawowej funkcji nie ma już w środowisku uruchomieniowym.
jcollum

180

Nie, taka metoda nie istnieje, ale napisanie jej jest trywialne:

func contains(s []int, e int) bool {
    for _, a := range s {
        if a == e {
            return true
        }
    }
    return false
}

Możesz użyć mapy, jeśli to wyszukiwanie jest ważną częścią twojego kodu, ale mapy też mają swoje koszty.


257
W rzeczywistości nie jest to trywialne, ponieważ musisz napisać po jednym dla każdego używanego typu, a ponieważ nie ma przeciążenia, musisz nazwać każdą funkcję inaczej, tak jak w C. append () może działać ogólnie, ponieważ ma specjalne wsparcie środowiska wykonawczego. Ogólny zawiera byłby użyteczny z tego samego powodu, ale tak naprawdę ogólnym rozwiązaniem jest po prostu ogólne wsparcie w języku.
Eloff,

15
@Eloffinterface{}
Alex Lockwood

2
@Alex Lockwood czy to faktycznie będzie działać z interfejsami?
Ory Band

101
trywialny == 7 linii kodu, w tym 1 pętla 1 gałąź if instrukcja i 1 porównanie? Myślę, że coś mi tu brakuje ...
tothemario,

3
Ale dlaczego nie dodać tych samych elementów do samego rdzenia?
Luna Lovegood


11

Zamiast używać slice, mapmoże być lepszym rozwiązaniem.

prosty przykład:

package main

import "fmt"


func contains(slice []string, item string) bool {
    set := make(map[string]struct{}, len(slice))
    for _, s := range slice {
        set[s] = struct{}{}
    }

    _, ok := set[item] 
    return ok
}

func main() {

    s := []string{"a", "b"}
    s1 := "a"
    fmt.Println(contains(s, s1))

}

http://play.golang.org/p/CEG6cu4JTf


34
W obecnej formie ten kod nie przynosi żadnych korzyści, ponieważ nie ma sensu budować mapy z wycinka, jeśli zamierzasz go użyć tylko raz. - Aby był przydatny, ten kod powinien raczej zapewniać funkcję, sliceToMapktóra wykonuje wszystkie przygotowania. Następnie wyszukiwanie mapy jest banalne i wydajne.
Roland Illig,

9

Rodzaj pakiet zawiera budulcem jeśli plaster jest posortowana czy jesteś gotów go uporządkować.

input := []string{"bird", "apple", "ocean", "fork", "anchor"}
sort.Strings(input)

fmt.Println(contains(input, "apple")) // true
fmt.Println(contains(input, "grow"))  // false

...

func contains(s []string, searchterm string) bool {
    i := sort.SearchStrings(s, searchterm)
    return i < len(s) && s[i] == searchterm
}

SearchStringzapowiada powrót the index to insert x if x is not present (it could be len(a)), więc sprawdzenie tego ujawnia, czy łańcuch zawiera posortowany plasterek.


Jeśli chodzi o czas, regularne wyszukiwanie jest możliwe O(n)i dzięki temu rozwiązaniu O(n*log(n)).
plesiv

@plesiv to wyszukiwanie binarne, AFAICS. Czy to nie oznaczałoby, że O (log n)?
Henrik Aasted Sørensen

tak, wyszukiwanie binarne i funkcja containsO(log(n)), ale ogólne podejście O(n*log(n))wynika z tego rodzaju.
plesiv

3

Możesz użyć pakietu odbicia do iteracji po interfejsie, którego konkretnym typem jest plasterek:

func HasElem(s interface{}, elem interface{}) bool {
    arrV := reflect.ValueOf(s)

    if arrV.Kind() == reflect.Slice {
        for i := 0; i < arrV.Len(); i++ {

            // XXX - panics if slice element points to an unexported struct field
            // see https://golang.org/pkg/reflect/#Value.Interface
            if arrV.Index(i).Interface() == elem {
                return true
            }
        }
    }

    return false
}

https://play.golang.org/p/jL5UD7yCNq


3
Jasne, że możesz użyć pakietu odbicia, ale tylko dlatego, że możesz, nie oznacza to, że powinieneś. Odbicie jest bardzo drogie.
Justin Ohms

3

Jeśli nie można użyć mapy do znalezienia przedmiotów opartych na kluczu, możesz rozważyć narzędzie goderive . Goderive generuje specyficzną dla typu implementację metody zawiera, dzięki czemu kod jest czytelny i wydajny.

Przykład;

type Foo struct {
    Field1 string
    Field2 int
} 

func Test(m Foo) bool {
     var allItems []Foo
     return deriveContainsFoo(allItems, m)
}

Aby wygenerować metodę deriveContainsFoo:

  • Zainstaluj goderive za pomocą go get -u github.com/awalterschulze/goderive
  • Uruchom goderive ./...w folderze obszaru roboczego

Ta metoda zostanie wygenerowana dla DeriveContains:

func deriveContainsFoo(list []Foo, item Foo) bool {
    for _, v := range list {
        if v == item {
            return true
        }
    }
    return false
}

Goderive obsługuje wiele innych przydatnych metod pomocniczych, pozwalających na zastosowanie funkcjonalnego stylu programowania.


2
func Contain(target interface{}, list interface{}) (bool, int) {
    if reflect.TypeOf(list).Kind() == reflect.Slice || reflect.TypeOf(list).Kind() == reflect.Array {
        listvalue := reflect.ValueOf(list)
        for i := 0; i < listvalue.Len(); i++ {
            if target == listvalue.Index(i).Interface() {
                return true, i
            }
        }
    }
    if reflect.TypeOf(target).Kind() == reflect.String && reflect.TypeOf(list).Kind() == reflect.String {
        return strings.Contains(list.(string), target.(string)), strings.Index(list.(string), target.(string))
    }
    return false, -1
}

2

Nie jestem pewien, czy potrzebne są tutaj leki generyczne. Potrzebujesz tylko umowy na swoje pożądane zachowanie. Wykonanie następujących czynności jest niczym więcej niż tym, co musiałbyś zrobić w innych językach, jeśli chcesz, aby własne obiekty zachowywały się same w kolekcjach, na przykład poprzez zastąpienie Equals () i GetHashCode ().

type Identifiable interface{
    GetIdentity() string
}

func IsIdentical(this Identifiable, that Identifiable) bool{
    return (&this == &that) || (this.GetIdentity() == that.GetIdentity())
}

func contains(s []Identifiable, e Identifiable) bool {
    for _, a := range s {
        if IsIdentical(a,e) {
            return true
        }
    }
    return false
}

1
„nie jest niczym więcej niż to, co musiałbyś robić w innych językach” nie jest tak naprawdę prawdą - np. w C # Contains()jest zaimplementowany List<T>, więc musisz tylko zaimplementować Equals()tę pracę.
George


0

Może to być uważane za nieco „hacky”, ale w zależności od wielkości i zawartości wycinka możesz połączyć wycinek i przeprowadzić wyszukiwanie łańcuchowe.

Na przykład masz plasterek zawierający wartości pojedynczych słów (np. „Tak”, „nie”, „może”). Te wyniki są dołączane do wycinka. Jeśli chcesz sprawdzić, czy ten plasterek zawiera jakieś wyniki „być może”, możesz użyć

exSlice := ["yes", "no", "yes", "maybe"]
if strings.Contains(strings.Join(exSlice, ","), "maybe") {
  fmt.Println("We have a maybe!")
}

To, jak odpowiednie jest to naprawdę, zależy od wielkości plastra i długości jego elementów. Mogą występować problemy z wydajnością lub przydatnością w przypadku dużych wycinków lub długich wartości, ale w przypadku mniejszych wycinków o skończonych rozmiarach i prostych wartościach można uzyskać pożądany wynik tylko w jednym wierszu.


Nie będzie działać w sytuacji, gdy elementy mają podobny tekst, ale nie dokładnie taki samexSlice := ["yes and no", "maybe", "maybe another"]
Raees Iqbal

Jest to dość przyjemne podejście do uzyskania szybkiego i brudnego rozwiązania z jedną linią. Musisz tylko wymagać jednoznacznego separatora (może być przecinkiem) i wykonać dodatkową pracę, aby ","+strings.Join(exSlice,",")+","",maybe,"
spakować

-1

Styl Go:

func Contains(n int, match func(i int) bool) bool {
    for i := 0; i < n; i++ {
        if match(i) {
            return true
        }
    }
    return false
}


s := []string{"a", "b", "c", "o"}
// test if s contains "o"
ok := Contains(len(s), func(i int) bool {
    return s[i] == "o"
})

2
To nie odpowiada na pytanie ani nie zawiera dodatkowych informacji.
Croolman
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.