2016-05-22 5 views
3

私はスライスからアイテムを削除する関数removeFromを持っています。同じ機能で異なる種類のスライスを受け入れる方法は?

func removeFrom(slice []float64, index int) []float64 { 
    if len(slice) > index { 
     return append(slice[:index], slice[index+1:]...) 
    } 
} 

それは正常に動作しますが、今、私はまた、整数のスライスから削除する必要があります:それはfloat64スライスとインデックスを受け入れます。だから、私は両方の型を受け入れるようにこれを変更することができます(そして与えられた型のスライスを返します)?私は、空のインターフェイスを使用しようとしたが、明らかに私は関数内でいくつかの変換を行う必要があり、私はそれを行う方法を見つけませんでした。

答えて

3

、何の「共通の祖先は、」すべてのスライスタイプのためにありません([]interface{}は、「互換性」ではありませんたとえば[]intとなります(詳細はCannot convert []string to []interface {}を参照してください)。

したがって、関数に任意のスライスタイプを受け入れる場合は、interface{}(「受信」パラメータと戻り値の両方のタイプ)を使用する必要があります。しかし、スライスを適用することができず、組み込まれたappend()関数に渡すことができない(インタフェース)ラッパー値があります。

既知のタイプにはtype assertiontype switchesを使用できますが、それぞれのコードを繰り返す必要があります。実際には先を進めるものではありません。

実際には、すべてのスライスタイプで機能するremoveFrom()関数を作成し、反射を使用して作成する方法があります。

reflect.Valueは、任意のGo値を記述するタイプです。これには、スライスを含む様々なGo型の値をサポートするメソッドがあります。

func (v Value) Slice(i, j int) Value 

我々はスライスをスライスするためにそれを使用することができます。私たちに興味深いのは何

Value.Slice()方法です。良い。これは、要素削除アルゴリズムの重要なポイントです。まだ必要なのは、取り外し可能な要素の前と後の2つのスライスに「結合」することです。幸いにもreflectパッケージには、このためのサポートを持っている:reflect.AppendSlice():最後の残りのキーとして

func AppendSlice(s, t Value) Value 

を、我々は、任意のスライスの長さを取得するためにValue.Len()を使用することができます。

func removeFrom(s interface{}, idx int) interface{} { 
    if v := reflect.ValueOf(s); v.Len() > idx { 
     return reflect.AppendSlice(v.Slice(0, idx), v.Slice(idx+1, v.Len())).Interface() 
    } 
    return s 
} 

本当に、それがすべてです:

は、我々は今、驚くほどシンプルで、弊社の一般的なremoveFrom()機能のために必要なのはすべてを持っています。それをテストする:

for i := 0; i < 4; i++ { 
    fmt.Println(removeFrom([]int{0, 1, 2}, i), "missing:", i) 
} 
for i := 0; i < 4; i++ { 
    fmt.Println(removeFrom([]string{"zero", "one", "two"}, i), "missing:", i) 
} 

出力(Go Playground上でそれを試してみてください):

[1 2] missing: 0 
[0 2] missing: 1 
[0 1] missing: 2 
[0 1 2] missing: 3 
[one two] missing: 0 
[zero two] missing: 1 
[zero one] missing: 2 
[zero one two] missing: 3 

注:

このソリューションは、リフレクションを使用していますので、それは別のソリューションよりも遅くはないだろうが反射を使用しているが、具体的な、サポートされているタイプは「有線」である。クイックベンチマークでは、この一般的なソリューションは、ワイヤードタイプの非反射型よりも2.5倍遅いことが示されています。パフォーマンスや利便性の向上/一般的な解決が重要であるかどうかを考慮する必要があります。または、これを具体的なタイプと組み合わせることができます。タイプスイッチを追加して頻繁なタイプを処理し、実際の具体的なタイプがタイプスイッチによって処理されない場合にのみこの一般的なソリューションに戻すことができます。

+0

それが私が探していたものです。ありがとう! – abel

+0

これは恐ろしいことに、タイプアサーションと比較して遅いことに注意してください。 – OneOfOne

+0

@OneOfOneこの一般的な解決法は反射を使用しているので、具体的なタイプが「配線されている」ものよりも遅くなります。 「恐ろしく」という言葉は強烈な言葉ですが、私はその違いが大きさのオーダーであると期待しています。クイックベンチマークでは、一般的な反射ソリューションが2.5倍遅くなっていることが示されています。 – icza

3

短答?できません。

長い答え、あなたはまだそれを直接行うことはできませんが、:Goはジェネリックをサポートしていません

func removeFrom(slice interface{}, index int) interface{} { 
    switch slice := slice.(type) { 
    case []float64: 
     if len(slice) > index { 
      return append(slice[:index], slice[index+1:]...) 
     } 
    case []int64: 
     if len(slice) > index { 
      return append(slice[:index], slice[index+1:]...) 
     } 
    case []int: 
     if len(slice) > index { 
      return append(slice[:index], slice[index+1:]...) 
     } 
    default: 
     log.Panicf("unknown type: %T", slice) 
    } 
} 
+1

うわー、それぞれのタイプごとに別々の方法を持つ方が良いと思います。P ありがとう! – abel

+0

'case [] float64、[] int64、[] int:doSmt'はどうですか? –

+0

上記のコードを実行しようとしましたが、 'invalid argument slice(type interface {})がlen'に対して... – abel

関連する問題