如何在Rust中复制Haskell的`scanl(+)0 xs`?

Lis*_*one 6 haskell rust

如果我有一个数字列表,[1, 2, 3, 4, 5]并且想生成一个累积总数列表,那么在Haskell中,我将执行以下操作:

> let xs = [1, 2, 3, 4, 5]

> scanl (+) 0 xs
[0,1,3,6,10,15]
Run Code Online (Sandbox Code Playgroud)

在Rust中,尝试获得相同的行为似乎不必要地麻烦。

let xs = [1, 2, 3, 4, 5];

let vs = vec![0]
    .into_iter()
    .chain(xs.iter().scan(0, |acc, x| {
        *acc += x;
        Some(*acc)
    }))
    .collect::<Vec<_>>();
Run Code Online (Sandbox Code Playgroud)

scan缺少GC可以解释必须使累加器发生突变的尴尬行为。但是,scan还不包括初始累加器值,因此需要在前面手动添加0。这本身很麻烦,因为我需要用加前缀chain[0].iter()没有工作,也没有[0].into_iter()vec![0].iter()。它需要vec![0].into_iter()

我觉得我在这里一定做错了。但是呢 有没有更好的方法来产生累计和?回到for循环了吗?

Öme*_*den 9

编辑:

尽管此答案的旧版本模仿了scanl中间形式的行为,但执行并不懒惰。用@French Boiethios的答案更新了我以前的答案中的通用实现。

这是实现:

fn scanl<'u, T, F>(op: F, initial: T, list: &'u [T]) -> impl Iterator<Item = T> + 'u
where
    F: Fn(&T, &T) -> T + 'u,
{
    let mut iter = list.iter();
    std::iter::successors(Some(initial), move |acc| iter.next().map(|n| op(n, acc)))
}
//scanl(|x, y| x + y, 0, &[1, 2, 3, 4, 5]).collect::<Vec<_>>()
Run Code Online (Sandbox Code Playgroud)

操场


它可以很容易地实现 fold

对于Add操作:

let result = xs.iter().fold(vec![0], |mut acc, val| {
    acc.push(val + acc.last().unwrap());
    acc
});
Run Code Online (Sandbox Code Playgroud)

操场


这是通用版本:

fn scanl<T, F>(op: F, initial: T, list: &[T]) -> Vec<T>
where
    F: Fn(&T, &T) -> T,
{
    let mut acc = Vec::with_capacity(list.len());
    acc.push(initial);

    list.iter().fold(acc, |mut acc, val| {
        acc.push(op(val, acc.last().unwrap()));
        acc
    })
}
//scanl(|x, y| x + y, 0, &[1, 2, 3, 4, 5])
Run Code Online (Sandbox Code Playgroud)

操场


Fre*_*ios 7

我会这样做successors

fn main() {
    let mut xs = vec![1, 2, 3, 4, 5].into_iter();
    let vs = std::iter::successors(Some(0), |acc| xs.next().map(|n| n + *acc));

    assert_eq!(vs.collect::<Vec<_>>(), [0, 1, 3, 6, 10, 15]);
}
Run Code Online (Sandbox Code Playgroud)