มีวิธีการสำหรับชิ้น


คำตอบ:


226

Mostafa ได้ชี้ให้เห็นแล้วว่าวิธีการดังกล่าวเป็นเรื่องเล็กน้อยที่จะเขียนและ mkb ให้คำแนะนำให้คุณใช้การค้นหาแบบไบนารีจากแพคเกจการเรียงลำดับ แต่ถ้าคุณจะทำเช็คจำนวนมากเช่นนั้นคุณอาจลองใช้แผนที่แทน

มันสำคัญมากที่จะตรวจสอบว่ามีรหัสแผนที่เฉพาะอยู่หรือไม่โดยใช้value, ok := yourmap[key]สำนวน เนื่องจากคุณไม่สนใจคุณค่าคุณอาจสร้างmap[string]struct{}ตัวอย่างเช่น การใช้ที่ว่างเปล่าstruct{}ที่นี่มีข้อได้เปรียบที่ไม่ต้องการพื้นที่เพิ่มเติมและประเภทแผนที่ภายในของ Go ได้รับการปรับให้เหมาะสมสำหรับค่าประเภทนั้น ดังนั้นจึงmap[string] struct{}เป็นตัวเลือกยอดนิยมสำหรับชุดในโลก Go


27
โปรดทราบว่าคุณต้องเขียนstruct{}{}เพื่อรับค่าของโครงสร้างที่ว่างเปล่าเพื่อให้คุณสามารถส่งมันไปยังแผนที่ของคุณเมื่อคุณต้องการเพิ่มองค์ประกอบ ลองใช้และถ้าคุณประสบปัญหาใด ๆ อย่าลังเลที่จะถาม นอกจากนี้คุณยังสามารถใช้โซลูชัน Mostafa ถ้าคุณเข้าใจได้ง่ายขึ้น (เว้นแต่คุณจะมีข้อมูลจำนวนมาก)
tux21b

5
การแก้ปัญหานั้นง่ายนั่นคือความจริง แต่สิ่งที่ต้องใช้ในการเพิ่มฟังก์ชั่นพื้นฐานดังกล่าวใน runtime ฉันไม่พบปัญหาดังกล่าวใน Go repo บน github มันน่าเศร้าและแปลก
Igor Petrov

1
วิธีการที่ไม่เปรียบเทียบกับmap[string] bool ดูเหมือนแฮ็คโดยเฉพาะอย่างยิ่งการเริ่มต้นโครงสร้างว่างmap[string] struct{}map[string] struct{}struct {}{}
vadasambar

@IgorPetrov เห็นด้วยฉันประหลาดใจที่คุณลักษณะพื้นฐานไม่ได้อยู่ในรันไทม์แล้ว
jcollum

180

ไม่ได้ไม่มีวิธีการดังกล่าว แต่เป็นเรื่องเล็กน้อยที่จะเขียน:

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

คุณสามารถใช้แผนที่ได้หากการค้นหานั้นเป็นส่วนสำคัญของรหัสของคุณ แต่แผนที่ก็มีค่าใช้จ่ายด้วย


257
ที่จริงแล้วมันไม่สำคัญเพราะคุณต้องเขียนหนึ่งชนิดสำหรับแต่ละประเภทที่คุณใช้และเนื่องจากไม่มีการโหลดมากเกินไปคุณต้องตั้งชื่อฟังก์ชั่นแต่ละอย่างแตกต่างกันเช่นใน C. append () สามารถทำงานได้ทั่วไปเพราะมีการสนับสนุนรันไทม์พิเศษ การบรรจุทั่วไปจะมีประโยชน์สำหรับเหตุผลเดียวกัน แต่จริงๆแล้วการแก้ปัญหาทั่วไปเป็นเพียงการสนับสนุนทั่วไปในภาษา
Eloff

15
@Eloffinterface{}
Alex Lockwood

2
@Alex Lockwood จะใช้งานกับอินเตอร์เฟสได้จริงหรือ
วง Ory

101
trivial == 7 บรรทัดของโค้ดรวมถึง 1 loop 1 branch ถ้า statement และ 1 เปรียบเทียบ? ฉันคิดว่าฉันหายไปบางสิ่งบางอย่างที่นี่ ...
tothemario

3
แต่ทำไมไม่เพิ่มสิ่งเหล่านี้เข้าไปในแกนตัวเอง?
Luna Lovegood

16

หากชิ้นจะถูกจัดเรียงมีการค้นหาแบบไบนารีดำเนินการในแพคเกจsort


11

แทนการใช้slice, mapอาจจะเป็นทางออกที่ดีกว่า

ตัวอย่างง่ายๆ:

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
ในรูปแบบปัจจุบันรหัสนี้ไม่มีประโยชน์เนื่องจากไม่มีจุดในการสร้างแผนที่จากชิ้นถ้าคุณจะใช้เพียงครั้งเดียว - เพื่อให้มีประโยชน์รหัสนี้ควรมีฟังก์ชั่นsliceToMapที่ใช้ในการเตรียมการทั้งหมด หลังจากนั้นการสืบค้นแผนที่นั้นเล็กน้อยและมีประสิทธิภาพ
Roland Illig

9

เรียงแพคเกจให้การก่อสร้างตึกถ้าชิ้นของคุณจะถูกจัดเรียงหรือคุณยินดีที่จะจัดเรียง

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
}

SearchStringสัญญาว่าจะส่งคืนthe index to insert x if x is not present (it could be len(a))ดังนั้นการตรวจสอบของที่แสดงให้เห็นว่าสตริงที่มีการจัดเรียงชิ้น


ในแง่ของเวลา, การค้นหาปกติและการแก้ไขปัญหานี้จะทำให้O(n) O(n*log(n))
plesiv

@plesiv มันเป็นการค้นหาแบบไบนารี AFAICS มันจะไม่ทำให้มันเป็น O (บันทึก n)?
Henrik Aasted Sørensen

ใช่การค้นหาแบบไบนารี่และฟังก์ชั่นcontainsต่างO(log(n))ๆ แต่วิธีการโดยรวมนั้นO(n*log(n))เกิดจากการเรียงลำดับ
plesiv

3

คุณสามารถใช้แพคเกจreflectเพื่อวนซ้ำอินเทอร์เฟซที่มีชนิดคอนกรีตเป็นชิ้น:

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
แน่นอนว่าคุณสามารถใช้แพ็คเกจการสะท้อนได้ แต่เพราะคุณทำได้ไม่ได้หมายความว่าคุณควรทำ ภาพสะท้อนมีราคาแพงมาก
Justin Ohms

3

ถ้ามันเป็นไปไม่ได้ที่จะใช้แผนที่เพื่อค้นหารายการที่ขึ้นอยู่กับคีย์คุณสามารถพิจารณาเครื่องมือgoderive Goderive สร้างการใช้งานเฉพาะของประเภท method ซึ่งทำให้โค้ดของคุณสามารถอ่านได้และมีประสิทธิภาพ

ตัวอย่าง;

type Foo struct {
    Field1 string
    Field2 int
} 

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

วิธีสร้างเมธอด deriveContainsFoo:

  • ติดตั้ง goderive ด้วย go get -u github.com/awalterschulze/goderive
  • เรียกใช้goderive ./...ในโฟลเดอร์เวิร์กสเปซของคุณ

วิธีนี้จะถูกสร้างขึ้นสำหรับ deriveContains:

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

Goderive มีการสนับสนุนวิธีการช่วยเหลือที่มีประโยชน์อื่น ๆ อีกมากมายที่จะใช้รูปแบบการเขียนโปรแกรมการทำงานในระหว่างการเดินทาง


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

ไม่แน่ใจว่าจำเป็นต้องใช้ยาชื่อสามัญที่นี่ คุณเพียงแค่ต้องทำสัญญาสำหรับพฤติกรรมที่คุณต้องการ การทำสิ่งต่อไปนี้ไม่ใช่แค่สิ่งที่คุณต้องทำในภาษาอื่น ๆ หากคุณต้องการให้วัตถุของคุณทำงานเป็นกลุ่มโดยการเอาชนะ Equals () และ 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
"ไม่มากไปกว่าสิ่งที่คุณต้องทำในภาษาอื่น" ไม่เป็นความจริง - เช่นใน C # Contains()มีการใช้งานList<T>ดังนั้นคุณจะต้องนำไปใช้Equals()กับงานนั้นเท่านั้น
George

1

ฉันสร้างเกณฑ์มาตรฐานง่าย ๆ ด้วยวิธีแก้ปัญหาจากคำตอบเหล่านี้

https://gist.github.com/NorbertFenk/7bed6760198800207e84f141c41d93c7

มันไม่ใช่มาตรฐานที่แท้จริงเพราะในตอนแรกฉันยังไม่ได้ใส่องค์ประกอบมากเกินไป แต่อย่าลังเลที่จะแยกและเปลี่ยนมัน


ฉันคิดเกี่ยวกับมัน แต่มันไม่ได้เป็นตัวแทนเพราะเครื่องของฉันไม่ได้มีประสิทธิภาพมาก
F. Norbert

0

อาจถือว่าเป็น 'แฮ็ค' เล็กน้อย แต่ขึ้นอยู่กับขนาดและเนื้อหาของชิ้นคุณสามารถเข้าร่วมชิ้นด้วยกันและทำการค้นหาสตริง

ตัวอย่างเช่นคุณมีชิ้นที่มีค่าคำเดียว (เช่น "ใช่", "ไม่", "อาจจะ") ผลลัพธ์เหล่านี้ผนวกเข้ากับส่วน หากคุณต้องการตรวจสอบว่าส่วนนี้มีผลลัพธ์ "อาจจะ" คุณอาจใช้

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

วิธีที่เหมาะสมนี้ขึ้นอยู่กับขนาดของส่วนและความยาวของสมาชิก อาจมีปัญหาเรื่องประสิทธิภาพหรือความเหมาะสมสำหรับชิ้นขนาดใหญ่หรือค่าความยาว แต่สำหรับชิ้นเล็ก ๆ ที่มีขนาด จำกัด และค่าอย่างง่ายมันเป็นหนึ่งซับที่ถูกต้องเพื่อให้ได้ผลลัพธ์ที่ต้องการ


จะไม่ทำงานสำหรับสถานการณ์ที่องค์ประกอบมีข้อความคล้ายกัน แต่ไม่เหมือนกันทั้งหมดexSlice := ["yes and no", "maybe", "maybe another"]
Raees Iqbal

นี่เป็นวิธีการที่ดีมากในการบรรลุวิธีแก้ปัญหาหนึ่งซับที่รวดเร็วและสกปรก คุณเพียงแค่ต้องใช้ตัวคั่นที่ไม่คลุมเครือ (อาจเป็นเครื่องหมายจุลภาค) และทำงานพิเศษเพื่อรองรับทั้งสองสตริง: ","+strings.Join(exSlice,",")+","และ",maybe,"
ขุนนาง

-1

สไตล์ไป:

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
สิ่งนี้ไม่ได้ตอบคำถามหรือให้ข้อมูลเพิ่มเติม
Croolman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.