Bir dilim yöntemi içerir


Yanıtlar:


226

Mostafa zaten böyle bir yöntemin yazmak için önemsiz olduğunu belirtti ve mkb size sıralama paketinden ikili aramayı kullanmanız için bir ipucu verdi. Ancak, bu tür birçok kontroller yapacaksanız, bunun yerine bir harita kullanmayı da düşünebilirsiniz.

Deyimi kullanarak belirli bir harita anahtarının mevcut olup olmadığını kontrol etmek önemsizdir value, ok := yourmap[key]. Değerle ilgilenmediğiniz map[string]struct{}için, örneğin bir tane de oluşturabilirsiniz . struct{}Burada boş bir alan kullanmanın ek alan gerektirmemesi ve Go'nun dahili harita türünün bu tür değerler için optimize edilmesi avantajı vardır. Bu nedenle, map[string] struct{}Go dünyasındaki setler için popüler bir seçimdir.


27
Ayrıca, struct{}{}bir öğe eklemek istediğinizde haritanıza aktarabilmeniz için boş yapının değerini almak için yazmanız gerektiğini unutmayın . Sadece deneyin ve herhangi bir sorunla karşılaşırsanız sormaya çekinmeyin. Eğer daha kolay anlaşılırsa (büyük miktarda veriye sahip değilseniz) Mostafa'nın çözümünü de kullanabilirsiniz.
tux21b

5
Çözüm basit, bu doğru. Ancak bu temel işlevleri çalışma zamanına eklemek için ne gerekir? Go repo'da github'da böyle sorunlar bulamadım. Bu üzücü ve garip.
Igor Petrov

1
İle nasıl map[string] boolkarşılaştırılır map[string] struct{}. map[string] struct{}özellikle boş bir yapı struct {}{}
başlatan

@IgorPetrov kabul etti, böyle bir temel özellik zaten çalışma zamanında olmadığına şaşırdım.
jcollum

180

Hayır, böyle bir yöntem mevcut değildir, ancak yazmak önemsizdir:

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

Bu arama kodunuzun önemli bir parçasıysa, ancak haritaların da maliyeti varsa bir harita kullanabilirsiniz.


257
Aslında önemsiz değil, çünkü kullandığınız her tür için bir tane yazmanız gerekiyor ve aşırı yükleme olmadığından, her bir fonksiyonu farklı bir şekilde adlandırmanız gerekiyor, çünkü C. append () özel çalışma zamanı desteğine sahip olduğundan genel olarak çalışabilir. Genel bir içerik aynı nedenden dolayı yararlı olacaktır, ancak gerçekten genel çözüm sadece dilde jenerik desteği.
Eloff

15
@Eloffinterface{}
Alex Lockwood

2
@Alex Lockwood aslında arayüzlerle çalışacak mı?
Ory Band

101
trivial == 1 döngü 1 dal eğer ifade ve 1 karşılaştırma dahil 7 kod satırı? Sanırım burada bir şey eksik ...
tothemario

3
Ama neden bunları go core içine eklemiyoruz?
Luna Lovegood


11

Yerine a kullanmanın slice, mapdaha iyi bir çözüm olabilir.

basit bir örnek:

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
Geçerli biçiminde bu kod hiçbir fayda sağlamaz, çünkü yalnızca bir kez kullanacaksanız bir dilimden bir harita oluşturmanın bir anlamı yoktur. - Yararlı olması için, bu kod sliceToMaptüm hazırlıkları yapan bir işlev sağlamalıdır . Bundan sonra haritayı sorgulamak önemsiz ve etkilidir.
Roland Illig

9

Sıralama senin dilim sıralı veya bunu sıralamak için istekli olup olmadığını paketi bina blokları sağlar.

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
}

SearchStringgeri dönmeyi vaat eder the index to insert x if x is not present (it could be len(a)), bu nedenle bir kontrol dizenin sıralanan dilimi içerip içermediğini gösterir.


Zaman açısından, düzenli arama yapılır O(n)ve bu çözüm bunu yapar O(n*log(n)).
plesiv

@plesiv Bu bir ikili arama, AFAICS. Bu O (log n) yapmaz mı?
Henrik Aasted Sørensen

evet, ikili arama ve işlev containsvardır O(log(n)), ancak genel yaklaşım O(n*log(n))sıralamaya bağlıdır.
plesiv

3

Yansıtma paketini, beton türü bir dilim olan bir arabirim üzerinde yinelemek için kullanabilirsiniz :

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
Tabii yansıma paketini kullanabilirsiniz, ancak yapabileceğiniz için yapmanız gerektiği anlamına gelmez. Yansıma çok pahalıdır.
Justin Ohms

3

Bir anahtara dayalı öğeleri bulmak için bir harita kullanmak mümkün değilse, goderive aracını düşünebilirsiniz . Goderive, içerme yönteminin türe özgü bir uygulamasını oluşturur ve kodunuzu hem okunabilir hem de verimli hale getirir.

Misal;

type Foo struct {
    Field1 string
    Field2 int
} 

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

DeriveContainsFoo yöntemini oluşturmak için:

  • Goderive uygulamasını yükle go get -u github.com/awalterschulze/goderive
  • Çalışma goderive ./...alanı klasörünüzde çalıştırın

Bu yöntem deriveContains için oluşturulacaktır:

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

Goderive, işlevsel bir programlama stili uygulamak için diğer bazı yararlı yardımcı yöntemleri destekliyor.


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

Burada jeneriklerin gerekli olduğundan emin değilim. İstediğiniz davranış için bir sözleşmeye ihtiyacınız var. Aşağıdakileri yapmak, örneğin Equals () ve GetHashCode () öğelerini geçersiz kılarak kendi nesnelerinizin koleksiyonlarda kendilerini hareket etmesini istiyorsanız, diğer dillerde yapmanız gerekenden daha fazlası değildir.

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
"Artık başka dillerde yapmak zorunda ne daha" gerçekten doğru değil - örneğin C # Contains()uygulandığını List<T>yalnızca hiç uygulamak zorunda, Equals()o iş için.
George


0

Biraz 'hacky' olarak kabul edilebilir, ancak dilimin boyutuna ve içeriğine bağlı olarak, dilime birlikte katılabilir ve bir dize araması yapabilirsiniz.

Örneğin, tek kelime değerleri içeren bir diliminiz var (örneğin, "evet", "hayır", "belki"). Bu sonuçlar bir dilime eklenir. Bu dilimin herhangi bir "belki" sonucu içerip içermediğini kontrol etmek istiyorsanız,

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

Bunun ne kadar uygun olduğu gerçekten dilimin büyüklüğüne ve üyelerinin uzunluğuna bağlıdır. Büyük dilimler veya uzun değerler için performans veya uygunluk sorunları olabilir, ancak daha küçük sonlu boyutlu dilimler ve basit değerler için, istenen sonucu elde etmek için geçerli bir astardır.


Öğelerin benzer metne sahip olduğu ancak tam olarak aynı olmadığı durumlar için çalışmazexSlice := ["yes and no", "maybe", "maybe another"]
Raees Iqbal

Bu, hızlı ve kirli tek astarlı bir çözüm elde etmek için oldukça hoş bir yaklaşımdır. Sadece kesin bir ayırıcıya ihtiyacınız var (virgül olabilir) ve her iki dizeyi parantez içine almak için fazladan iş yapmanız gerekir: ","+strings.Join(exSlice,",")+","ve",maybe,"
nobar

-1

Git tarzı:

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
Bu soruya cevap vermez veya ek bilgi vermez.
Croolman
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.