web-dev-qa-db-ja.com

RustのVecからアイテムを取得するにはどうすればよいですか?

consumesVecで、Vecswap_removeのようにremoveの不変条件を復元するオーバーヘッドなしに、1つの要素を返すメソッドを探しています。

fn take<T>(vec: Vec<T>, index: usize) -> Option<T>

しかし、そのような方法は見つかりません。私は何かが足りないのですか?これは実際に安全ではないのですか、それとも不可能ですか?

これは Vec <T>から移動するための*安全な*方法で組み込まれていますか? とは異なる質問です。目標は、範囲外のアクセスでパニックにならず、removeを返すResultメソッドでした。 Vecを消費し、要素の1つを返すメソッドを探しています。上記の質問に対する答えはどれも私の質問に対応していません。

11
Others

次のように関数を書くことができます:

_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())。TDropを実装する場合、drop()ベクトル内にあるすべての要素に対して呼び出されます。つまり、ベクトルの削除はO(n)であることが保証されます。TDropを実装しない場合、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)操作です!上記で説明したように、ベクトルを削除するのに必要な時間は言うまでもありません。

11

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つの方法で実現できます。

  1. _swap_remove_次に、各要素を繰り返し処理して要素を破棄し、
  2. 特定のindexをスキップして、各要素を繰り返し処理して要素を破棄します。

後者が前者よりも速いかどうかは私にはわかりません。どちらかといえば、より複雑に見え、より多くのブランチがあり(2つのループをお勧めします)、予測子が失われ、ベクトル化の影響を受けにくくなる可能性があります。

次に、Vecの不変条件を復元するオーバーヘッドについて文句を言う前に、適切にprofiledソリューションを実行しましたか?

_swap_remove_バリアントを見ると、次の3つのステップがあります。

  1. _swap_remove_(O(1))、
  2. 残りの各要素(O(N))を破壊し、
  3. バッキングメモリを解放します。

要素にDropの実装がない場合、ステップ2は最適化される可能性がありますが、そうでない場合は、(2)または(3)のどちらがコストを支配しているかが問題になります。

TL; DR:ゴーストの問題と戦っているのではないかと思います、profile最適化を試みる前に。

4
Matthieu M.