首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Rust中复制Haskell的“`scanl (+) 0 xs`”?

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

Stack Overflow用户
提问于 2019-06-14 05:12:59
回答 3查看 595关注 0票数 7

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

代码语言:javascript
复制
> let xs = [1, 2, 3, 4, 5]

> scanl (+) 0 xs
[0,1,3,6,10,15]

试图得到同样的行为似乎在铁锈不必要的麻烦。

代码语言:javascript
复制
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<_>>();

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

我觉得我一定是在做错事。但是,什么?是否有更好的方法来产生一个累积的总和?返回到for循环了吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-06-14 07:54:44

我会用successors来做

代码语言:javascript
复制
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]);
}
票数 8
EN

Stack Overflow用户

发布于 2019-06-14 05:37:52

编辑:

尽管这个答案的旧版本模仿了scanl中间形式的行为,但执行并不懒惰。用@法语Boiethios的回答更新了我以前的答案中的一般实现。

这就是执行情况:

代码语言:javascript
复制
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<_>>()

游乐场

它可以很容易地由fold实现。

对于Add操作:

代码语言:javascript
复制
let result = xs.iter().fold(vec![0], |mut acc, val| {
    acc.push(val + acc.last().unwrap());
    acc
});

游乐场

以下是通用版本:

代码语言:javascript
复制
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])

游乐场

票数 9
EN

Stack Overflow用户

发布于 2019-06-14 13:38:18

必须改变累加器的尴尬扫描行为可以用缺少GC来解释。

没有什么能阻止铁锈做你要做的事。

可能实施的例子:

代码语言:javascript
复制
pub struct Mapscan<I, A, F> {
    accu: Option<A>,
    iter: I,
    f: F,
}

impl<I, A, F> Mapscan<I, A, F> {
    pub fn new(iter: I, accu: Option<A>, f: F) -> Self {
        Self { iter, accu, f }
    }
}

impl<I, A, F> Iterator for Mapscan<I, A, F>
where
    I: Iterator,
    F: FnMut(&A, I::Item) -> Option<A>,
{
    type Item = A;

    fn next(&mut self) -> Option<Self::Item> {
        self.accu.take().map(|accu| {
            self.accu = self.iter.next().and_then(|item| (self.f)(&accu, item));
            accu
        })
    }
}

trait IterPlus: Iterator {
    fn map_scan<A, F>(self, accu: Option<A>, f: F) -> Mapscan<Self, A, F>
    where
        Self: Sized,
        F: FnMut(&A, Self::Item) -> Option<A>,
    {
        Mapscan::new(self, accu, f)
    }
}

impl<T: ?Sized> IterPlus for T where T: Iterator {}

fn main() {
    let xs = [1, 2, 3, 4, 5];

    let vs = xs
        .iter()
        .map_scan(Some(0), |acc, x| Some(acc + x));

    assert_eq!(vs.collect::<Vec<_>>(), [0, 1, 3, 6, 10, 15]);
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56591730

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档