Chứa phương thức cho một lát


Câu trả lời:


226

Mostafa đã chỉ ra rằng một phương pháp như vậy là tầm thường để viết và mkb đã cho bạn một gợi ý để sử dụng tìm kiếm nhị phân từ gói sắp xếp. Nhưng nếu bạn định làm nhiều kiểm tra như vậy, bạn cũng có thể cân nhắc sử dụng bản đồ thay thế.

Việc kiểm tra xem một khóa bản đồ cụ thể có tồn tại hay không bằng cách sử dụng value, ok := yourmap[key]thành ngữ này. Vì bạn không quan tâm đến giá trị, bạn cũng có thể tạo một map[string]struct{}ví dụ. Sử dụng một khoảng trống struct{}ở đây có lợi thế là nó không yêu cầu bất kỳ không gian bổ sung nào và loại bản đồ nội bộ của Go được tối ưu hóa cho loại giá trị đó. Do đó, map[string] struct{}là một lựa chọn phổ biến cho các bộ trong thế giới Go.


27
Cũng lưu ý rằng bạn phải viết struct{}{}để lấy giá trị của cấu trúc trống để bạn có thể chuyển nó vào bản đồ của mình khi bạn muốn thêm một phần tử. Chỉ cần thử nó, và nếu bạn gặp bất kỳ vấn đề, hãy hỏi. Bạn cũng có thể sử dụng giải pháp của Mostafa nếu bạn dễ hiểu hơn (trừ khi bạn có lượng dữ liệu khổng lồ).
tux21b

5
Giải pháp rất đơn giản, đó là sự thật. Nhưng những gì nó cần để thêm chức năng cơ bản như vậy vào thời gian chạy? Tôi chưa tìm thấy những vấn đề như vậy trong Go repo trên github. Điều đó thật buồn và lạ.
Igor Petrov

1
Làm thế nào để map[string] boolso sánh với map[string] struct{}. map[string] struct{}có vẻ như một vụ hack đặc biệt là khởi tạo một cấu trúc trốngstruct {}{}
vadasambar

@IgorPetrov đã đồng ý, tôi ngạc nhiên khi một tính năng cơ bản như vậy không có trong thời gian chạy.
jcollum

179

Không, phương pháp như vậy không tồn tại, nhưng không quan trọng để viết:

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

Bạn có thể sử dụng bản đồ nếu việc tra cứu đó là một phần quan trọng trong mã của bạn, nhưng bản đồ cũng có chi phí.


257
Trên thực tế, điều đó không tầm thường, vì bạn phải viết một loại cho mỗi loại bạn sử dụng và vì không có quá tải, bạn phải đặt tên cho từng chức năng khác nhau, như trong C. append () có thể hoạt động chung vì nó có hỗ trợ thời gian chạy đặc biệt. Một chứa chung chung sẽ hữu ích cho cùng một lý do, nhưng thực sự giải pháp chung chỉ là hỗ trợ chung trong ngôn ngữ.
Eloff

15
@Eloffinterface{}
Alex Lockwood

2
@Alex Lockwood điều này thực sự sẽ làm việc với các giao diện?
Ban nhạc Ory

101
tầm thường == 7 dòng mã bao gồm 1 vòng lặp 1 nhánh nếu câu lệnh và 1 so sánh? Tôi nghĩ rằng tôi đang thiếu một cái gì đó ở đây ...
tothemario

3
Nhưng tại sao không thêm những thứ này vào lõi?
Luna Lovegood

16

Nếu lát được sắp xếp, có một tìm kiếm nhị phân được thực hiện trong các sortgói .


11

Thay vì sử dụng một slice, mapcó thể là một giải pháp tốt hơn.

ví dụ đơn giản:

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
Ở dạng hiện tại, mã này không mang lại lợi ích gì, vì không có điểm nào trong việc xây dựng bản đồ từ một lát nếu bạn chỉ sử dụng nó một lần. - Để có ích, mã này nên cung cấp một chức năng sliceToMapthực hiện tất cả các bước chuẩn bị. Sau đó, truy vấn bản đồ là chuyện nhỏ và hiệu quả.
Roland Illig

9

Các loại gói cung cấp các khối xây dựng nếu lát của bạn là sắp xếp hoặc bạn sẵn sàng để sắp xếp nó.

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
}

SearchStringhứa sẽ quay trở lại the index to insert x if x is not present (it could be len(a)), vì vậy kiểm tra xem có cho biết chuỗi có chứa lát cắt đã sắp xếp hay không.


Về thời gian, tìm kiếm thường xuyên O(n)và giải pháp này làm cho nó O(n*log(n)).
xin vui lòng

@plesiv đó là một tìm kiếm nhị phân, AFAICS. Nó sẽ không làm cho nó O (log n)?
Henrik Aast Sørenen

có, tìm kiếm nhị phân và chức năng containsO(log(n)), nhưng cách tiếp cận tổng thể là O(n*log(n))do sắp xếp.
xin vui lòng

3

Bạn có thể sử dụng gói phản chiếu để lặp lại trên một giao diện có loại cụ thể là một lát:

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
Chắc chắn bạn có thể sử dụng gói phản chiếu nhưng chỉ vì bạn có thể, không có nghĩa là bạn nên. Phản xạ là rất tốn kém.
Justin Ohms

3

Nếu việc sử dụng bản đồ để tìm vật phẩm dựa trên khóa là không khả thi, bạn có thể xem xét việc đỡ đầu công cụ. Goderive tạo ra một kiểu triển khai cụ thể của phương thức chứa, làm cho mã của bạn vừa dễ đọc vừa hiệu quả.

Thí dụ;

type Foo struct {
    Field1 string
    Field2 int
} 

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

Để tạo phương thức deriveContainsFoo:

  • Cài đặt tin tưởng với go get -u github.com/awalterschulze/goderive
  • Chạy goderive ./...trong thư mục không gian làm việc của bạn

Phương thức này sẽ được tạo cho deriveContains:

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

Goderive đã hỗ trợ khá nhiều phương thức trợ giúp hữu ích khác để áp dụng phong cách lập trình chức năng.


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

Không chắc chắn thuốc generic là cần thiết ở đây. Bạn chỉ cần một hợp đồng cho hành vi mong muốn của bạn. Thực hiện các thao tác sau không nhiều hơn những gì bạn sẽ phải làm trong các ngôn ngữ khác nếu bạn muốn các đối tượng của mình tự hành xử trong các bộ sưu tập, bằng cách ghi đè Equals () và GetHashCode () chẳng hạn.

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
"không nhiều hơn những gì bạn sẽ phải làm trong các ngôn ngữ khác" không thực sự đúng - ví dụ: trong C # Contains()được triển khai List<T>, vì vậy bạn chỉ phải thực hiện Equals()cho công việc đó.
George

1

Tôi đã tạo ra một điểm chuẩn rất đơn giản với các giải pháp từ những câu trả lời này.

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

Đó không phải là một điểm chuẩn thực sự bởi vì ban đầu, tôi đã không chèn quá nhiều yếu tố nhưng cảm thấy thoải mái khi rẽ nhánh và thay đổi nó.


Tôi đã nghĩ về nó nhưng nó không phải là đại diện vì thực tế máy của tôi không mạnh lắm.
F. Norbert

0

Nó có thể được coi là một chút 'hacky' nhưng tùy thuộc vào kích thước và nội dung của lát cắt, bạn có thể tham gia lát cắt với nhau và thực hiện tìm kiếm chuỗi.

Ví dụ: bạn có một lát chứa các giá trị từ đơn (ví dụ: "có", "không", "có thể"). Những kết quả này được nối vào một lát. Nếu bạn muốn kiểm tra xem lát này có chứa bất kỳ kết quả "có thể" nào không, bạn có thể sử dụng

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

Làm thế nào phù hợp điều này thực sự phụ thuộc vào kích thước của lát và chiều dài của các thành viên của nó. Có thể có các vấn đề về hiệu suất hoặc sự phù hợp cho các lát lớn hoặc giá trị dài, nhưng đối với các lát nhỏ hơn có kích thước hữu hạn và các giá trị đơn giản thì đó là một lớp lót hợp lệ để đạt được kết quả mong muốn.


Sẽ không hoạt động trong trường hợp các thành phần có văn bản tương tự nhưng không hoàn toàn giống nhauexSlice := ["yes and no", "maybe", "maybe another"]
Raees Iqbal

Đây là một cách tiếp cận khá hay để đạt được giải pháp một lớp lót nhanh và bẩn. Bạn chỉ cần yêu cầu một dấu phân cách rõ ràng (có thể là dấu phẩy) và thực hiện công việc bổ sung để đặt dấu ngoặc cho cả hai chuỗi: ","+strings.Join(exSlice,",")+","",maybe,"
tộc

-1

Phong cách đi:

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
Điều này không trả lời câu hỏi, cũng không cung cấp thêm thông tin.
Croolman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.