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

时间:2019-06-14 05:12:59

标签: haskell rust

如果我有一个数字列表Saks Fifth Avenue: 40; International: 6; OFF 5TH: ,并且想生成一个累积和列表,那么在Haskell中,我将执行以下操作:

[1, 2, 3, 4, 5]

在Rust中,尝试获得相同的行为似乎很麻烦。

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

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

必须更改累加器的笨拙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<_>>(); 行为可以通过缺少GC来解释。但是,scan还不包含初始累加器值,因此需要在前面手动添加0。这本身很麻烦,因为我需要在其前面加上scanchain,但是[0].iter()[0].into_iter()也不起作用。它需要vec![0].iter()

我觉得我在这里一定做错了。但是呢有没有更好的方法来产生累计和?它回到vec![0].into_iter()循环了吗?

3 个答案:

答案 0 :(得分:9)

编辑:

尽管此答案的旧版本模仿SELECT `mytable`.`id`, `mytable`.`title`, `mytable`.`cover_photo`, `mytable`.`city_id`, `mytable`.`city_name`, `cities`.`name` AS `master_city_name`, `mytable`.`state_id`, `mytable`.`state_name`, `states`.`name` AS `master_state_name`, `countries`.`id` AS `country_id`, `countries`.`name` AS `country_name`, subquery.priority FROM mytable left join cities on cities.id = mytable.city_id left join states on states.id = mytable.state_id left join countries on countries.id = mytable.country_id left join users on users.id = mytable.user_id left join ( SELECT `mytable`.`id`, '7' as priority FROM `mytable` LEFT JOIN `cities` ON `cities`.`id` = `mytable`.`city_id` WHERE `mytable`.`city_id` = '161' UNION SELECT `mytable`.`id`, '8' as priority FROM `mytable` LEFT JOIN `states` ON `states`.`id` = `mytable`.`state_id` WHERE `mytable`.`state_id` = '12' UNION SELECT `mytable`.`id`, '9' as priority FROM `mytable` LEFT JOIN `countries` ON `countries`.`id` = `mytable`.`country_id` WHERE `mytable`.`country_id` = '64' ) AS subquery on subquery.id = mytable.id where users.active = 1 and subquery.priority is not NULL ORDER BY `subquery`.`priority`, RAND() LIMIT 25 的中间形式的行为,但执行并不懒惰。使用@French Boiethios's answer从我的旧答案中更新了通用实现。

这是实现:

scanl

Playground


可以通过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

Playground


以下是通用版本:

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

Playground

答案 1 :(得分:7)

我会用successors来做到这一点:

Ratio2 = a8/b9=0/9= 0

答案 2 :(得分:2)

  

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

没有什么可以阻止Rust执行您的要求。

可能的实现示例:

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]);
}