2013-09-02 19 views
9

Goが再び私を困惑させました。うまくいけば誰かが助けることができる。構造体(myStruct)へのポインタを含むスライス(mySlice)を作成しました。スライス上のメソッドを呼び出すことによってスライスからアイテムを削除する方法

問題は「削除」メソッドです。私たちが "Remove"の中にいるときはすべて正常ですが、一度戻ってしまうとスライスのサイズは変わっていないので、最後の要素が2回表示されます。

「追加」メソッドで使用されているのと同じパターンを使用して「削除」を作成しようとしましたが、コンパイルされず、コメントアウトされました。

新しく作成したスライスを呼び出し元の関数に返すことで動作させることができますが、mySlice(ms)がシングルトンであるため、これを実行したくありません。私はかどうかはわかりませんが、

そして、私はまだ十分に尋ねていなかった場合は...

「追加」メソッドのコードは、働いています。私が収集できるものから "Add"はスライスヘッダ(3つの項目 "struct")へのポインタを受け取ります。私が読んだところでは、スライスの長さと容量はメソッドに渡されません(値渡し時)ので、おそらくスライスへのポインタを渡すことでメソッドは長さと容量を見て使用することができます"追加"する。これが当てはまる場合、同じパターンが「削除」で機能しないのはなぜですか?

みなさんの洞察力と助けに感謝します。

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

と結果...

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

答えて

12

あなたは正しいRemove1()で初めてでした。 Removeはスライスのコピーを取得し、スライスの長さを変更することはできません。

削除機能の問題は、Goの操作の順序に従って、参照が解除される前にスライスが行われることです。

*slc = append(*slc[:item], *slc[item+1:]...)*slc = append((*slc)[:item], (*slc)[item+1:]...)に変更しました。

しかし、私は読みやすさと保守性のために、次のをお勧めします:

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

おかげで、あなたの迅速な返事スティーブンのために非常に多くの!ですから、関数/メソッドにスライスを渡すときに渡されるのは、スライスの3項目の構造体定義(決して基底の配列ではありません)だけです。私がスライス定義のCOPYを渡しているときに、値渡しのときには、私はスライス定義へのポインタを渡していますか?再度、感謝します! – user2736464

+0

修正。値を渡すと、スライスのlen、cap、およびバッキング配列へのポインタのコピーが得られます。スライスへのポインタを渡すと、オリジナルのlenとcapを変更できます。 –

1

スティーブン・ワインバーグが指摘したようアペンドは必ずしも、スライスを参照の同じアドレスを返しませんので。 この制限を回避する別の方法は、スライスをラップする構造体を定義することです。例えば

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

出力:

[]int{1, 2, 3} 
[]int{1, 2} 
[]int{1, 2, 4, 5, 6} 
関連する問題