Go มีคำว่า "if x in" เหมือนกับ Python หรือไม่


290

โดยไม่ต้องวนซ้ำทั้งอาร์เรย์ฉันจะตรวจสอบว่าxใน array ที่ใช้ Go ได้อย่างไร ภาษามีโครงสร้างหรือไม่?

ชอบ Python: if "x" in array: ...


2
อาจเป็นแบบ

7
AFAIK ไม่มีการจดชวเลขสำหรับการเดินทาง ภายในไพ ธ อนยังวนซ้ำแถวนั้นไม่มีสิ่งใดเกิดขึ้น
Danish94

6
BTW สิ่งสำคัญที่ควรทราบคือไม่สามารถทำสิ่งนี้ได้ (ตามที่คุณขอ) " [w] โดยไม่คำนึงถึงการวนซ้ำทั้งหมด" การทำลูปอย่างชัดเจน (หรือหลังฟังก์ชั่นเช่นstrings.Index) ช่วยทำให้ชัดเจนมากขึ้นว่าโค้ดกำลังทำอะไร ฉันได้รับความประทับใจที่บางทีคุณคิดว่า Python in array:กำลังทำอะไรเร็ว / เวทมนต์ AFAIK มันไม่ใช่ การทำให้ลูปชัดเจนช่วยให้ผู้เขียน (และผู้อ่านทั้งหมด) ทราบและพิจารณาการใช้งานอื่น ๆ (เช่นแผนที่)
Dave C

5
อย่างไรก็ตามหาก "x" ในชุดนั้นเร็วมาก
Roberto Alsina

6
ฉันไม่คิดว่าเรากำลังขอวิธีที่รวดเร็วแบบมีโปรแกรมเราแค่ขอให้กระชับ (และยังไม่ได้ ... )
Migwell

คำตอบ:


340

ไม่มีโอเปอเรเตอร์ในตัวที่จะดำเนินการใน Go คุณจะต้องวนซ้ำแถวลำดับ คุณสามารถเขียนฟังก์ชั่นของคุณเองเพื่อทำสิ่งนี้:

func stringInSlice(a string, list []string) bool {
    for _, b := range list {
        if b == a {
            return true
        }
    }
    return false
}

หากคุณต้องการตรวจสอบความเป็นสมาชิกโดยไม่ต้องวนซ้ำทั้งรายการคุณต้องใช้แผนที่แทนการใช้อาร์เรย์หรือชิ้นดังนี้:

visitedURL := map[string]bool {
    "http://www.google.com": true,
    "https://paypal.com": true,
}
if visitedURL[thisSite] {
    fmt.Println("Already been here.")
}

4
มีวิธีการทำเช่นนี้โดยไม่ระบุประเภท? สมมติว่าถ้าผมต้องการเพียงทั่วไป needleInHaystack (เข็มกองหญ้า) ฟังก์ชั่นโดยไม่ต้องใช้วิธีการที่แยกต่างหากสำหรับทุกชนิดเดียว
อัลเลน

26
มันสามารถทำได้ด้วยแพคเกจสะท้อน แต่มันจะไม่มีประสิทธิภาพพอสมควร (อาจจะค่อนข้างช้าเหมือนกับที่คุณเขียนมันออกมาในภาษาไดนามิกเช่น Python) นอกเหนือจากนั้นไม่มี นั่นคือสิ่งที่ผู้คนหมายถึงเมื่อพวกเขาพูดว่าโกไม่มีข้อมูลทั่วไป
andybalholm

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

4
คุณไม่สามารถจัดเรียงแผนที่ (วัตถุ) ใน Javascript ด้วย มันเป็นข้อผิดพลาด v8 ที่วัตถุส่งคืนค่าตามลำดับตัวอักษร
kumarharsh

7
แผนที่ไม่ได้ถูกจัดเรียงในภาษาส่วนใหญ่ซึ่งถือว่าเป็นส่วนหนึ่งของหลักสูตรสำหรับโครงสร้างข้อมูลแผนที่ (hashmap)
Hejazzman

101

วิธีแก้ไขอื่นหากรายการมีค่าคงที่

เช่นการตรวจสอบค่าที่ถูกต้องจากรายการค่าที่ถูกต้อง:

func IsValidCategory(category string) bool {
    switch category {
    case
        "auto",
        "news",
        "sport",
        "music":
        return true
    }
    return false
}

12
ใช่จะเกิดอะไรขึ้นถ้า "ค่าที่ใช้ได้" ของคุณมาจากฐานข้อมูล
Rami Dabain

ใช่นี่เป็นระเบียบ แต่เมื่อสามารถกำหนดค่าเหล่านี้ล่วงหน้าเท่านั้น
piggybox

3
@ RonanDejhero จากนั้นฉันสามารถใช้ WHERE: myValue IN (แบบสอบถามย่อย) :)
anilech

3
นี่เป็นระเบียบเรียบร้อยเมื่อเทียบกับคำตอบยอดนิยม
piggybox

50

นี่คือคำพูดจากหนังสือ "Programming in Go: การสร้างแอปพลิเคชันสำหรับศตวรรษที่ 21":

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

Go จัดให้มีวิธีการ sort.Search () ซึ่งใช้อัลกอริธึมการค้นหาแบบไบนารี: สิ่งนี้ต้องการการเปรียบเทียบรายการ log2 (n) เท่านั้น (โดยที่ n คือจำนวนรายการ) ในแต่ละครั้ง หากต้องการระบุในมุมมองนั้นการค้นหาแบบเชิงเส้นของ 1000000 รายการต้องใช้การเปรียบเทียบ 500000 รายการโดยเฉลี่ยด้วยกรณีที่เลวร้ายที่สุดของการเปรียบเทียบ 1000000 รายการ การค้นหาแบบไบนารี่ต้องการการเปรียบเทียบมากที่สุด 20 ครั้งแม้ในกรณีที่แย่ที่สุด

files := []string{"Test.conf", "util.go", "Makefile", "misc.go", "main.go"}
target := "Makefile"
sort.Strings(files)
i := sort.Search(len(files),
    func(i int) bool { return files[i] >= target })
if i < len(files) && files[i] == target {
    fmt.Printf("found \"%s\" at files[%d]\n", files[i], i)
}

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


10
ซึ่งจะสมเหตุสมผลถ้าคุณทำการค้นหาซ้ำ มิฉะนั้นคุณจะมีความซับซ้อน n * log (n) * log (n) สำหรับการเรียงลำดับและการค้นหาแบบไบนารีเทียบกับเพียงแค่ n สำหรับการค้นหาเชิงเส้น
คริสเตียน

1
ที่จริงแล้วเป็นเพียงn*log(n) + log(n)เพราะมันเป็นสองปฏิบัติการอิสระที่ตามมา
pomo_mondreganto

27

ตัวอย่างข้างต้นโดยใช้การเรียงลำดับอยู่ใกล้ แต่ในกรณีของสตริงใช้เพียง SearchString:

files := []string{"Test.conf", "util.go", "Makefile", "misc.go", "main.go"}
target := "Makefile"
sort.Strings(files)
i := sort.SearchStrings(files, target)
if i < len(files) && files[i] == target {
    fmt.Printf("found \"%s\" at files[%d]\n", files[i], i)
}

https://golang.org/pkg/sort/#SearchStrings


2
คำตอบนี้ดูเหมือนจะเป็นสำเนาคัดลอกที่ให้ข้อมูลน้อยกว่าของคำตอบด้านล่างซึ่งเกิดขึ้นก่อนคำตอบนี้
cytinus

@cytinus คุณหมายถึงคำตอบอะไร sort.SearchStringsนั่นเป็นเพียงคนเดียวที่ผมเห็นอยู่บนพื้นฐานของ
akim

1
ฉันได้รับความเร็ว 100x จากการค้นหาซ้ำ ๆ ผ่านชิ้นส่วนขนาดใหญ่
Xeoncross

20

เพิ่งมีคำถามที่คล้ายกันและตัดสินใจลองใช้คำแนะนำในกระทู้นี้

ฉันได้เปรียบเทียบสถานการณ์กรณีที่ดีที่สุดและเลวร้ายที่สุดของการค้นหา 3 ประเภท:

  • ใช้แผนที่
  • ใช้รายการ
  • ใช้คำสั่งเปลี่ยน

นี่คือรหัสฟังก์ชั่น:

func belongsToMap(lookup string) bool {
list := map[string]bool{
    "900898296857": true,
    "900898302052": true,
    "900898296492": true,
    "900898296850": true,
    "900898296703": true,
    "900898296633": true,
    "900898296613": true,
    "900898296615": true,
    "900898296620": true,
    "900898296636": true,
}
if _, ok := list[lookup]; ok {
    return true
} else {
    return false
}
}


func belongsToList(lookup string) bool {
list := []string{
    "900898296857",
    "900898302052",
    "900898296492",
    "900898296850",
    "900898296703",
    "900898296633",
    "900898296613",
    "900898296615",
    "900898296620",
    "900898296636",
}
for _, val := range list {
    if val == lookup {
        return true
    }
}
return false
}

func belongsToSwitch(lookup string) bool {
switch lookup {
case
    "900898296857",
    "900898302052",
    "900898296492",
    "900898296850",
    "900898296703",
    "900898296633",
    "900898296613",
    "900898296615",
    "900898296620",
    "900898296636":
    return true
}
return false
}

สถานการณ์กรณีที่ดีที่สุดเลือกรายการแรกในรายการกรณีที่แย่ที่สุดใช้ค่า nonexistant

นี่คือผลลัพธ์:

BenchmarkBelongsToMapWorstCase-4 2000000 787 ns/op BenchmarkBelongsToSwitchWorstCase-4 2000000000 0.35 ns/op BenchmarkBelongsToListWorstCase-4 100000000 14.7 ns/op BenchmarkBelongsToMapBestCase-4 2000000 683 ns/op BenchmarkBelongsToSwitchBestCase-4 100000000 10.6 ns/op BenchmarkBelongsToListBestCase-4 100000000 10.4 ns/op

การสลับเปลี่ยนชนะทุกกรณีกรณีที่เลวร้ายที่สุดนั้นเร็วกว่ากรณีที่ดีที่สุด แผนที่เลวร้ายที่สุดและรายการใกล้จะเปลี่ยนมากขึ้น

ดังนั้นคุณธรรมคือ: ถ้าคุณมีรายการแบบคงที่ขนาดเล็กพอสมควรงบงบเปลี่ยนเป็นวิธีที่จะไป


ฉันไม่ทราบว่าจะใช้กรณีนี้ให้เหมาะสมหรือไม่ แต่จะสร้างความแตกต่างได้หรือไม่ถ้าคุณย้ายการกำหนดค่าเริ่มต้นของรายการ / แผนที่นอกฟังก์ชั่นทดสอบ
w00t

ฉันอยากรู้ว่าการเปรียบเทียบจะเล่นกับรายการที่เรียงลำดับอย่างไรและการเรียงลำดับนั้นจะคุ้มค่าไหม
Michael Draper

แล้ว:แทนที่จะเป็นอะไร,ในคำสั่ง switch มันทำให้เร็วขึ้นหรือไม่
Thomas Sauvajon

ฉันลองใช้หลายcaseงบแทนที่จะเป็นกรณีเดียว ผลลัพธ์จะสมเหตุสมผลเหมือนกันทั้งสองฟังก์ชั่น
โทมัสโซวาจอน

7

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

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

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    var MAX int = 10

    m := make(map[int]bool)

    for i := 0; i <= MAX; i++ {
        m[rand.Intn(MAX)] = true
    }

    for i := 0; i <= MAX; i++ {
        if _, ok := m[i]; ok {
            fmt.Printf("%v is in map\n", i)
        } else {
            fmt.Printf("%v is not in map\n", i)
        }
    }
}

นี่มันเป็นสนามเด็กเล่น


0

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

package main

import "fmt"

type StrSlice []string

func (list StrSlice) Has(a string) bool {
    for _, b := range list {
        if b == a {
            return true
        }
    }
    return false
}

func main() {
    var testList = StrSlice{"The", "big", "dog", "has", "fleas"}

    if testList.Has("dog") {
        fmt.Println("Yay!")
    }
}

ฉันมีห้องสมุดยูทิลิตี้ที่ฉันกำหนดสิ่งทั่วไปบางอย่างเช่นนี้สำหรับชิ้นส่วนหลายประเภทเช่นที่มีจำนวนเต็มหรือ structs อื่น ๆ ของฉันเอง

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.