如何在Rust中折叠向量的向量?

4
我有一个如下的二维向量。
    let m: Vec<Vec<u64>> = vec![
        vec![1, 2, 3, 4, 5],
        vec![1, 2, 3, 4, 5],
        vec![1, 2, 3, 4, 5],
        vec![1, 2, 3, 4, 5],
        vec![1, 2, 3, 4, 5]
    ];

增加一个函数add,旨在将两个向量的每个元素相加。

fn add(xs: &Vec<u64>, ys: &Vec<u64>) -> Vec<u64> {
    xs.iter().zip(ys.iter()).map(|(x, y)| x + y).collect()
}

现在,我想要对存储向量的向量 m 进行折叠操作。

我尝试过的方法是:

    let s: Vec<u64> = m[1..]
        .iter()
        .fold(m[0], |acc, xs| add(&acc, xs));

但是这段代码无法通过编译器。

   |
16 |         .fold(m[0], |acc, xs| add(&acc, xs));
   |               ^^^^ move occurs because value has type `Vec<u64>`, which does not implement the `Copy` trait

我尝试了使用&来前置,但编译器仍然拒绝:

   |
16 |         .fold(&m[0], |acc, xs| add(&acc, xs));
   |               ^^^^^ expected struct `Vec`, found `&Vec<u64>`
   |
   = note: expected struct `Vec<u64>`
           found reference `&Vec<u64>`
help: consider removing the borrow
   |
16 -         .fold(&m[0], |acc, xs| add(&acc, xs));
16 +         .fold(m[0], |acc, xs| add(&acc, xs));
   |

我认为add函数的签名是正确的,因为它不想接管参数向量的所有权,并返回一个新的向量,因此所有权应该传递给调用者。

实际上,我尝试了各种可能的组合(添加/删除函数中的&等),但无法使代码通过编译器。

你能告诉我错过了什么,上述代码有什么问题吗?谢谢。


fold的第一个参数必须与其输出类型相同。因此,传递类型为& Vec<u64>& m[0]将不起作用,因为您希望折叠返回Vec<u64>。一种选择是以m[0].clone()作为初始值开始。显然,这涉及到克隆,但无论如何,您都需要分配输出。 - gspr
注意:我知道这是一个老问题,但为了后人,请注意&Vec<T>是一种反模式:请使用&[T] - Chayim Friedman
2个回答

5
fold 的第一个参数必须与输出类型相同。因此,你建议传递类型为& Vec<u64>& m[0]是不起作用的,因为你希望fold返回Vec<u64>(请注意值与借用值)。而使用未借用的m[0]将无法工作,因为你会尝试从稍后在迭代本身中使用的向量中移动。
一种选择是以m[0].clone()作为初始值。这显然涉及克隆,但是你需要分配输出,所以无论如何都不能更好。这样可以解决问题。
let s: Vec<u64> = m[1..].iter().fold(m[0].clone(), |acc, xs| add(& acc, xs));

无关紧要的提示:我建议您将 add 的签名更改为更通用的形式 fn add(xs: & [u64], ys: & [u64]) -> Vec<u64>。您仍然可以按原来的方式使用它(因为& Vec<u64>可以强制转换为& [u64]),但是它更加通用,因为其他类型也可以强制转换为& [u64]


2
你提到添加 clone() 是其中一种选项。你能解释一下其他可能的选项吗? - ntalbs
@ntalbs:好问题!实际上,我没有看到更好的选择。结果向量需要在某个时刻分配。我认为唯一的其他合理选择是将初始值设置为零向量,然后对所有m进行折叠,但这并不比使用m[0]的克隆进行初始化更好。 - gspr

0

可以使用如下代码:

let first = m.pop().unwrap();
let s: Vec<u64> = m
        .iter()
        .fold(first, |acc, xs| add(&acc, xs));

这样可以避免克隆第一个参数。 它假定操作的顺序无关紧要。


1
并更改原始的 m - Chayim Friedman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接