、何の「共通の祖先は、」すべてのスライスタイプのためにありません([]interface{}
は、「互換性」ではありませんたとえば[]int
となります(詳細はCannot convert []string to []interface {}を参照してください)。
したがって、関数に任意のスライスタイプを受け入れる場合は、interface{}
(「受信」パラメータと戻り値の両方のタイプ)を使用する必要があります。しかし、スライスを適用することができず、組み込まれたappend()
関数に渡すことができない(インタフェース)ラッパー値があります。
既知のタイプにはtype assertionとtype 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倍遅いことが示されています。パフォーマンスや利便性の向上/一般的な解決が重要であるかどうかを考慮する必要があります。または、これを具体的なタイプと組み合わせることができます。タイプスイッチを追加して頻繁なタイプを処理し、実際の具体的なタイプがタイプスイッチによって処理されない場合にのみこの一般的なソリューションに戻すことができます。
それが私が探していたものです。ありがとう! – abel
これは恐ろしいことに、タイプアサーションと比較して遅いことに注意してください。 – OneOfOne
@OneOfOneこの一般的な解決法は反射を使用しているので、具体的なタイプが「配線されている」ものよりも遅くなります。 「恐ろしく」という言葉は強烈な言葉ですが、私はその違いが大きさのオーダーであると期待しています。クイックベンチマークでは、一般的な反射ソリューションが2.5倍遅くなっていることが示されています。 – icza