consumesVec
で、Vec
とswap_remove
のようにremove
の不変条件を復元するオーバーヘッドなしに、1つの要素を返すメソッドを探しています。
fn take<T>(vec: Vec<T>, index: usize) -> Option<T>
しかし、そのような方法は見つかりません。私は何かが足りないのですか?これは実際に安全ではないのですか、それとも不可能ですか?
これは Vec <T>から移動するための*安全な*方法で組み込まれていますか? とは異なる質問です。目標は、範囲外のアクセスでパニックにならず、remove
を返すResult
メソッドでした。 Vec
を消費し、要素の1つを返すメソッドを探しています。上記の質問に対する答えはどれも私の質問に対応していません。
次のように関数を書くことができます:
_fn take<T>(mut vec: Vec<T>, index: usize) -> Option<T> {
if vec.get(index).is_none() {
None
} else {
Some(vec.swap_remove(index))
}
}
_
ここでseeするコード(get
および_swap_remove
_)はO(1)が保証されています。
ただし、、一種の非表示、vec
は関数の最後でドロップされ、このドロップ操作はO(1)ではない可能性があります。ただし、O(n)(nはvec.len()
)。T
がDrop
を実装する場合、drop()
ベクトル内にあるすべての要素に対して呼び出されます。つまり、ベクトルの削除はO(n)であることが保証されます。T
がDrop
を実装しない場合、Vec
は割り当てを解除するだけで済みます。 dealloc
操作の時間の複雑さはアロケーターに依存し、指定されていないため、O(1)であると想定することはできません。
イテレータを使用した別のソリューションについて言及するには:
_fn take<T>(vec: Vec<T>, index: usize) -> Option<T> {
vec.into_iter().nth(index)
}
_
私はこれを書き込もうとしていました:
Iterator::nth()
は通常線形時間演算ですが、ベクトルに対するイテレーターはこのメソッドをオーバーライドしてO(1)操作。
しかし、それから私は、これがスライスを反復するイテレータにのみ当てはまることに気づきました。上記のコードで使用される_std::vec::IntoIter
_イテレータは、nth()
をオーバーライドしません。試みられました ここ ですが、それほど簡単ではないようです。
したがって、現時点では、上記のイテレータソリューションはO(n)操作です!上記で説明したように、ベクトルを削除するのに必要な時間は言うまでもありません。
fn take<T>(vec: Vec<T>, index: usize) -> Option<T>
が標準ライブラリに存在しない理由は、一般的にあまり役に立たないためです。たとえば、長さが10の_Vec<String>
_があるとすると、9つの文字列を破棄し、1つだけを使用することを意味します。これは無駄に思えます。
一般に、標準ライブラリは最大のシナリオで役立つAPIを提供しようとします。この場合、fn take<T>(vec: &mut Vec<T>, index: usize) -> Option<T>
を使用する方が論理的です。
もちろん、唯一の問題は不変条件をどのように保存するかです。
Vec::swap_remove
_が行うことです。Vec::drain
_が行うことです。それらは非常に柔軟性があり、あなたのようなより具体的なシナリオを満たすように適応させることができます。
_swap_remove
_の適応:
_fn take<T>(mut vec: Vec<T>, index: usize) -> Option<T> {
if index < vec.len() {
Some(vec.swap_remove(index))
} else {
None
}
}
_
drain
の適応:
_fn take<T>(mut vec: Vec<T>, index: usize) -> Option<T> {
if index < vec.len() {
vec.drain(index..index+1).next()
} else {
None
}
}
_
前者の方が効率的であることに注意してください。それはO(1)です。
Vec
と_swap_remove
_のようにVec
の不変条件を復元するオーバーヘッドなしに、remove
を消費して1つの要素を返すメソッドを探しています。
これは私にとって時期尚早のマイクロ最適化の悪臭です。
まず、ベクトルの要素を破棄する必要があることに注意してください。これは2つの方法で実現できます。
swap_remove
_次に、各要素を繰り返し処理して要素を破棄し、index
をスキップして、各要素を繰り返し処理して要素を破棄します。後者が前者よりも速いかどうかは私にはわかりません。どちらかといえば、より複雑に見え、より多くのブランチがあり(2つのループをお勧めします)、予測子が失われ、ベクトル化の影響を受けにくくなる可能性があります。
次に、Vec
の不変条件を復元するオーバーヘッドについて文句を言う前に、適切にprofiledソリューションを実行しましたか?
_swap_remove
_バリアントを見ると、次の3つのステップがあります。
swap_remove
_(O(1))、要素にDrop
の実装がない場合、ステップ2は最適化される可能性がありますが、そうでない場合は、(2)または(3)のどちらがコストを支配しているかが問題になります。
TL; DR:ゴーストの問題と戦っているのではないかと思います、profile最適化を試みる前に。