2013-09-02 41 views
9

Go đã làm tôi bối rối một lần nữa. Hy vọng rằng ai đó có thể giúp đỡ. Tôi đã tạo một slice (mySlice) có chứa các con trỏ tới các cấu trúc (myStruct).Cách xóa một mục khỏi một lát bằng cách gọi một phương thức trên slice

Sự cố là phương pháp "Xóa". Khi chúng ta ở bên trong "Remove" mọi thứ đều ổn, nhưng một khi chúng ta quay trở lại, kích thước slice đã không thay đổi, và vì vậy chúng ta thấy phần tử cuối cùng được liệt kê hai lần.

Ban đầu tôi đã thử viết "Xóa" bằng cách sử dụng cùng một mẫu được sử dụng trong phương thức "Thêm", nhưng nó sẽ không biên dịch và đã được nhận xét.

Tôi có thể làm cho nó hoạt động bằng cách trả lại lát mới được tạo cho hàm gọi, nhưng tôi không muốn làm điều này vì mySlice (ms) là một singleton.

Và nếu tôi không hỏi đủ đã ...

Mã cho phương pháp "Add" đang làm việc, mặc dù tôi không chắc chắn như thế nào. Từ những gì tôi có thể thu thập "Thêm" là nhận được một con trỏ đến tiêu đề slice (3 mục "struct"). Từ những gì tôi đã đọc, chiều dài và dung lượng của một slice không được truyền cho các phương thức (khi truyền theo giá trị), vì vậy có lẽ việc truyền con trỏ tới slice cho phép phương thức xem và sử dụng độ dài và dung lượng. để "nối thêm". Nếu điều này là đúng, thì tại sao cùng một mẫu không hoạt động trong "Xóa"?

Cảm ơn rất nhiều vì thông tin chi tiết và trợ giúp của mọi người!

package main 

import (
    "fmt" 
) 

type myStruct struct { 
    a int 
} 
type mySlice []*myStruct 

func (slc *mySlice) Add(str *myStruct) { 
    *slc = append(*slc, str) 
} 

//does not compile with reason: cannot slice slc (type *mySlice) 
//func (slc *mySlice) Remove1(item int) { 
// *slc = append(*slc[:item], *slc[item+1:]...) 
//} 

func (slc mySlice) Remove(item int) { 
    slc = append(slc[:item], slc[item+1:]...) 
    fmt.Printf("Inside Remove = %s\n", slc) 
} 

func main() { 
    ms := make(mySlice, 0) 
    ms.Add(&myStruct{0}) 
    ms.Add(&myStruct{1}) 
    ms.Add(&myStruct{2}) 
    fmt.Printf("Before Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms) 
    ms.Remove(1) //remove element 1 (which also has a value of 1) 
    fmt.Printf("After Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms) 
} 

và kết quả ...

Before Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{1}) %!s(*main.myStruct=&{2})] 

Inside Remove = [%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2})] 

After Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2}) %!s(*main.myStruct=&{2})] 

Trả lời

12

Bạn đã đúng lần đầu tiên với Remove1(). Hủy bỏ lấy một bản sao của slice và do đó không thể thay đổi độ dài của slice.

Sự cố trong chức năng xóa của bạn là theo thứ tự các phép toán trong Go, việc cắt đi trước khi dereferencing.

Khắc phục là thay đổi *slc = append(*slc[:item], *slc[item+1:]...) thành *slc = append((*slc)[:item], (*slc)[item+1:]...).

Tuy nhiên tôi muốn giới thiệu sau đây để có thể đọc và bảo trì:

func (slc *mySlice) Remove1(item int) { 
    s := *slc 
    s = append(s[:item], s[item+1:]...) 
    *slc = s 
} 
+0

Cảm ơn rất nhiều vì trả lời nhanh chóng của bạn Stephen! Vì vậy, chỉ cần làm rõ, khi truyền các slice tới các hàm/phương thức, thứ duy nhất được truyền qua là định nghĩa struct 3-lát của slice (không bao giờ là mảng cơ bản). Khi đi qua giá trị tôi đang đi qua một COPY của định nghĩa slice và khi đi qua sở thích tôi đang đi qua một con trỏ đến định nghĩa slice? Cảm ơn một lần nữa! – user2736464

+0

Chính xác. Khi bạn chuyển một giá trị, bạn sẽ nhận được một bản sao của len, nắp và một con trỏ tới mảng sao lưu. Khi bạn vượt qua một con trỏ đến slice, bạn có thể sửa đổi len và nắp của bản gốc. –

1

thêm sẽ không nhất thiết phải trở về cùng một địa chỉ tham chiếu đến lát, như Stephen Weinberg đã chỉ ra. Một cách khác để giải quyết vấn đề này là xác định một cấu trúc kết thúc tốt cho slice.

ví dụ:

package main 

import "fmt" 

type IntList struct { 
    intlist []int 
} 

func (il *IntList) Pop() { 
    if len(il.intlist) == 0 { return } 
    il.intlist = il.intlist[:len(il.intlist)-1] 
} 

func (il *IntList) Add(i... int) { 
    il.intlist = append(il.intlist, i...) 
} 

func (il *IntList) String() string { 
    return fmt.Sprintf("%#v",il.intlist) 
} 

func main() { 
    intlist := &IntList{[]int{1,2,3}} 
    fmt.Println(intlist) 
    intlist.Pop() 
    fmt.Println(intlist) 
    intlist.Add([]int{4,5,6}...) 
    fmt.Println(intlist) 
} 

đầu ra:

[]int{1, 2, 3} 
[]int{1, 2} 
[]int{1, 2, 4, 5, 6} 
Các vấn đề liên quan