개발하자

Go는 파이썬과 유사한 "if x in" 구조를 가지고 있나요?

Cuire 2023. 2. 13. 16:57
반응형

Go는 파이썬과 유사한 "if x in" 구조를 가지고 있나요?

Go를 사용하여 전체 어레이에서 어레이가 반복되는지 확인하려면 어떻게 해야 합니까? 언어에 이것에 대한 구조가 있나요?

파이썬과 같이:

if "x" in array: 
  # do something



Go 1.18+에서는 이제 에도 구현된 일반적인 포함 함수를 선언할 수 있습니다. 그것은 모든 유사한 유형에 적용된다

func Contains[T comparable](arr []T, x T) bool {
    for _, v := range arr {
        if v == x {
            return true
        }
    }
    return false
}

그리고 그것을 다음과 같이 사용한다:

if Contains(arr, "x") {
    // do something
}
// or
if slices.Contains(arr, "x") {
    // do something
}

내가 여기서 찾은 것




시도:

present := lo.Contains[int]([]int{0, 1, 2, 3, 4, 5}, 5)



바둑에는 그것을 할 수 있는 내장된 운영자가 없다. 배열을 반복해야 합니다. 다음과 같이 자신만의 기능을 작성할 수 있습니다:

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

또는 Go 1.18 이상에서 (golang.org/x/exp/slices)에서)를 사용할 수 있습니다.

전체 목록에서 반복하지 않고 구성원 자격을 확인하려면 다음과 같이 배열 또는 조각 대신 지도를 사용해야 합니다:

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



이것은 "프로그래밍 인 고: 21세기를 위한 응용 프로그램 만들기"라는 책에서 인용한 것이다:

이와 같은 단순 선형 검색은 정렬되지 않은 데이터에 대한 유일한 옵션이며 작은 조각(최대 수백 개의 항목)에도 좋습니다. 그러나 특히 검색을 반복적으로 수행하는 경우에는 선형 검색이 매우 비효율적이어서 매번 항목의 절반을 비교해야 합니다.

Go는 종류를 제공합니다.이진 검색 알고리즘을 사용하는 검색() 메서드: 이를 위해서는 매번 log2(n) 항목(여기서 n은 항목 수)만 비교해야 합니다. 이러한 관점에서 1000000 항목을 선형으로 검색하려면 평균 500000개의 비교가 필요하며 최악의 경우 1000000개의 비교가 필요합니다.

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




목록에 정적 값이 포함되어 있는 경우 다른 솔루션입니다.

예: 유효한 값 목록에서 유효한 값 확인:

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



정렬을 사용하는 위의 예제는 가깝지만 문자열의 경우 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/#검색 문자열




또 다른 옵션은 지도를 세트로 사용하는 것이다. 키만 사용하고 값이 항상 참인 부울과 같은 것이 됩니다. 그러면 지도에 키가 포함되어 있는지 쉽게 확인할 수 있습니다. 이 기능은 집합의 동작이 필요한 경우 유용하며, 값을 여러 번 추가하면 집합에 한 번만 추가됩니다.

여기 지도에 임의의 숫자를 추가하는 간단한 예가 있습니다. 동일한 번호가 두 번 이상 생성된 경우에는 최종 지도에 한 번만 표시됩니다. 그럼 지도에 키가 있는지 확인하기 위해 간단한 체크를 사용합니다.

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)
        }
    }
}

여기 바둑 운동장에 있어요




방금 비슷한 질문이 있어서 이 스레드에서 몇 가지 제안을 시도해 보기로 결정했습니다.

다음과 같은 세 가지 유형의 조회에 대한 최상의 시나리오와 최악의 시나리오를 벤치마킹했습니다:

  • 지도를 이용해서
  • 목록 사용
  • 스위치 문 사용

기능 코드는 다음과 같습니다:

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
}

최상의 경우 목록에서 첫 번째 항목을 선택하고 최악의 경우에는 존재하지 않는 값을 사용합니다.

결과는 다음과 같습니다:

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

스위치가 끝까지 이기며, 최악의 경우는 최선의 경우보다 월등히 빠릅니다.

지도는 최악이고 목록은 스위치에 더 가깝다.

그래서 교훈은 다음과 같다: 정적이고 상당히 작은 목록이 있는 경우에는 스위치 문을 사용하는 것이 좋습니다.




이것은 파이썬의 "in" 연산자의 자연스러운 느낌에 가장 가깝다. 당신은 당신만의 유형을 정의해야 합니다. 그런 다음 원하는 대로 동작하는 "have"와 같은 메서드를 추가하여 해당 유형의 기능을 확장할 수 있습니다.

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!")
    }
}

저는 유틸리티 라이브러리를 가지고 있습니다. 여기서 정수나 다른 구조를 포함하는 것과 같은 몇 가지 유형의 슬라이스에 대해 몇 가지 공통점을 정의합니다.

네, 선형 시간으로 작동하지만 그게 중요한 게 아닙니다. 바둑이 가진 공통어와 갖지 못한 공통어가 무엇인지 묻고 배우는 것이 포인트다. 그것은 좋은 운동이다. 이 대답이 어리석은지 유용한지는 독자에게 달려있다.


반응형