如何在Rust中基于另一个bool vector构造并有条件地分配给一个新的vector

tf7tbtn2  于 2023-04-30  发布在  其他
关注(0)|答案(1)|浏览(107)

我想构造一个新的向量(float64),用NaN填充,长度为N,然后将它的一部分分配给另一个基于boolean向量的向量(float64)。
这可以在python(numpy)中轻松完成,例如:

import numpy as np

N = 5

a = np.empty(N, dtype=float)
a.fill(np.nan)

b = [True, False, True, False, False]
c = [1.1, 5.3]

a[b] = c
# a = [1.1, nan, 5.3, nan, nan]

在Rust中实现这一点的惯用和有效的方法应该是什么?

vawmfj5a

vawmfj5a1#

Rust中并没有真正的唯一惯用的方式。有很多可能的方法。但实际上有几个最佳实践。最重要的一条是:

  • 使用迭代器(.iter())代替索引操作符([..])。索引运算符执行越界检查(因为Rust没有未定义的行为,越界访问将是未定义的行为),因此速度很慢(-ish)。

让我向你展示两种实现这一目标的可能方法。
如果您创建一个NaN数组,然后对值进行排序,那么同时进行这两个数组会更有效。像这样:(谢谢@Stargateur)

fn main() {
    let b = vec![true, false, true, false, false];
    let c = vec![1.1, 5.3];

    let mut i = c.into_iter();
    let d: Vec<_> = b
        .into_iter()
        .map(|cond| {
            cond.then_some(())
                .and_then(|_| i.next())
                .unwrap_or(f64::NAN)
        })
        .collect();

    println!("{d:?}");
}
[1.1, NaN, 5.3, NaN, NaN]

说明:

  • let mut i = c.into_iter()创建迭代器i,它将生成c的所有元素。
  • b.into_iter()创建一个迭代器,它将产生b的所有值。
  • .map()操作将布尔值Map到i.next()f64::NAN,这取决于它们的值。i.next()插入c的下一个值。
  • 注意i.next()闭包的一部分,这意味着它只在实际需要时才被计算。
  • .collect()获取所有值并将它们收集到一个向量中(在本例中)。

下面是另一个例子,当我们已经有了一个vector并希望在特定位置插入元素时:

fn main() {
    let mut data = [f64::NAN; 5];
    println!("{data:?}");

    let b = vec![true, false, true, false, false];
    let c = vec![1.1, 5.3];

    let mut c_values = c.into_iter();
    for (data_entry, b_value) in data.iter_mut().zip(b) {
        if b_value {
            if let Some(c_value) = c_values.next() {
                *data_entry = c_value;
            }
        }
    }

    println!("{data:?}");
}
[NaN, NaN, NaN, NaN, NaN]
[1.1, NaN, 5.3, NaN, NaN]

原理是类似的,但我们不是.collect() ing,而是通过.iter_mut()对其进行迭代,因此我们可以修改现有数据。

相关问题