Rust中的迭代器的使用:map转换filter过滤fold聚合chain链接

Posted Pomelo_刘金

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rust中的迭代器的使用:map转换filter过滤fold聚合chain链接相关的知识,希望对你有一定的参考价值。

什么是迭代器

Rust中的迭代器是一种强大的工具,它提供了一种灵活、通用的方法来遍历序列。迭代器是实现了Iterator trait的类型,并需要至少实现一个next函数,用于让迭代器指向下一个迭代对象,并返回一个Option用于指示对象是否存在。

fn next (&mut self) -> Option<Self::Item>;

迭代器相比于for循环有一些优势。首先,迭代器提供了一种灵活、通用的方法来迭代序列。它允许你使用各种方法来处理序列中的元素,例如map、filter、fold等。这些方法可以让你更简洁、更清晰地表达你的意图。

此外,迭代器和Rust的所有权系统密切相连。这意味着你可以使用迭代器来安全地处理序列中的元素,而不必担心内存安全问题。

迭代器是Rust零抽象之一,这意味着迭代器抽象不会引入运行时开销,不会有任何性能上的影响

迭代器可以做什么

Rust中的迭代器可以通过实现Iterator trait来创建,也可以通过调用现有类型的iter方法来获取。例如,Vec提供了一个iter方法,可以返回一个迭代器,用于遍历Vec中的元素。

let v = vec![1, 2, 3];
for i in v.iter() 
    println!("", i);


除了for循环外,迭代器还提供了许多其他有用的方法,例如: 迭代器模式允许你对一个项的序列进行某些处理。Rust中的迭代器提供了一种简洁、高效的方式来处理序列,例如通过使用map、filter、fold等方法来转换、过滤和聚合数据。这些方法通常比手写循环更简洁、更易读,也更容易优化。

  • map:转换数据。接受一个闭包并为迭代器中的每个元素调用该闭包,然后返回一个新的迭代器,其中包含闭包返回的值。
let v = vec![1, 2, 3];
let v_squared: Vec<i32> = v.iter().map(|x| x * x).collect();

  • filter:过滤数据。接受一个闭包并为迭代器中的每个元素调用该闭包。如果闭包返回true,则元素将包含在新的迭代器中。
let v = vec![1, 2, 3];
let v_even: Vec<&i32> = v.iter().filter(|x| *x % 2 == 0).collect();

  • fold:聚合数据。接受一个初始值和一个闭包,并将闭包应用于初始值和迭代器中的每个元素,以生成一个单一的最终值。
let v = vec![1, 2, 3];
let sum: i32 = v.iter().fold(0, |acc, x| acc + x);

  • chain:该方法是Iterator trait的一个方法,它允许你将两个迭代器链接在一起,形成一个新的迭代器。这个新的迭代器会先遍历第一个迭代器中的所有元素,然后再遍历第二个迭代器中的所有元素。

例如,你可以使用chain方法将两个数组中的元素链接在一起:

let a = [1, 2, 3];
let b = [4, 5];
let c: Vec<i32> = a.iter().chain(b.iter()).copied().collect();
assert_eq!(c, [1, 2, 3, 4, 5]);

在这个例子中,我们创建了两个数组a和b,然后使用chain方法将它们链接在一起,形成一个新的迭代器。最后,我们使用collect方法将迭代器中的元素收集到一个向量中。

希望这对你有所帮助。 ## 如何创建一个迭代器 要创建一个迭代器,你需要给一个类型实现Iterator trait,并实现next方法。下面是一个例子,它展示了如何在一个斐波那契数列类型上创建迭代器:

struct Fib 
    a: i32,
    b: i32,


impl Iterator for Fib 
    type Item = i32;

    fn next(&mut self) -> Option<Self::Item> 
        let res = self.a;
        self.a = self.b;
        self.b = res + self.b;
        Some(res)
    


fn main() 
    let fib = Fib  a: 1, b: 1 ;
    for i in fib.take(10) 
        println!("", i);
    


这个例子中,我们定义了一个Fib结构体,它包含两个字段a和b。然后我们为Fib结构体实现了Iterator trait,并实现了next方法。在next方法中,我们计算出下一个斐波那契数,并返回它。最后,在main函数中,我们创建了一个Fib实例,并使用take方法获取前10个斐波那契数1

使用迭代器要注意什么

在使用Rust中的迭代器时,有几点需要注意:

  • 迭代器是惰性的:迭代器不会立即计算它们的值,而是在调用next方法时才会计算。这意味着你需要调用collect或其他消耗迭代器的方法来获取最终结果,这里有一个详细的例子来解释迭代器的惰性:
fn main() 
    let v = vec![1, 2, 3];
    let v_iter = v.iter().map(|x| 
        println!("Mapping value: ", x);
        x * 2
    );

    println!("Created iterator");

    for val in v_iter 
        println!("Got value: ", val);
    


在这个例子中,我们创建了一个迭代器v_iter,它使用map方法将序列中的每个元素乘以2。注意,在创建迭代器时,我们并没有看到任何输出。这是因为迭代器是惰性的,它不会立即计算它们的值。

接下来,我们使用for循环来打印出计算后结果。在这个过程中,我们可以看到输出。这是因为for循环会调用迭代器的next方法来获取下一个值。在调用next方法时,迭代器才会计算它的值。

  • 注意所有权和借用:当你使用迭代器时,需要注意所有权和借用规则。例如,如果你想要在迭代器中修改元素,你需要使用iter_mut而不是iter方法。
let mut v = vec![1, 2, 3];
for i in v.iter_mut() 
   *i *= 2;


  • 注意迭代器失效:当你修改了迭代器所指向的集合时,迭代器可能会失效。例如,如果你在遍历Vec的同时向其中添加元素,可能会导致迭代器失效。 下面是一个例子,它展示了如何在遍历Vec的同时向其中添加元素,导致迭代器失效:
fn main() 
    let mut v = vec![1, 2, 3];
    let mut v_iter = v.iter_mut();

    while let Some(val) = v_iter.next() 
        println!("Got value: ", val);
        if *val == 2 
            v.push(4);
        
    


在这个例子中,我们创建了一个可变迭代器v_iter来遍历Vec。然后我们使用while循环来遍历迭代器。在遍历过程中,当我们遇到值为2的元素时,我们向Vec中添加了一个新元素。

然而,在Rust中,这样的操作是不允许的。当你运行这段代码时,你会得到一个运行时错误,提示你迭代器已经失效。

  • 注意性能问题:虽然迭代器通常比手写循环更简洁、更易读,但它们并不总是最快的。如果性能至关重要,你应该测试不同的实现方式,并选择最快的一种。 例如,下面是两个计算1到10的和的例子:
fn main() 
    let sum: i32 = (1..11).sum();
    println!("", sum);

    let mut sum = 0;
    for i in 1..11 
        sum += i;
    
    println!("", sum);


第一个例子使用了迭代器来计算和,而第二个例子使用了手写循环。在大多数情况下,这两个例子的性能差异并不明显。但是,在某些情况下,手写循环可能会比迭代器更快。单数使用迭代器比使用for循环更简洁,并且保证内存安全

总之,Rust中的迭代器是一种强大的工具,它提供了一种简洁、高效、安全的方式来操作数据。在使用迭代器时,应注意惰性、所有权和借用、迭代器失效和性能问题。 from刘金,转载请注明原文链接。感谢!

  • 注意性能问题:虽然迭代器通常比手写循环更简洁、更易读,但它们并不总是最快的。如果性能至关重要,你应该测试不同的实现方式,并选择最快的一种。 例如,下面是两个计算1到10的和的例子:
fn main() 
    let sum: i32 = (1..11).sum();
    println!("", sum);

    let mut sum = 0;
    for i in 1..11 
        sum += i;
    
    println!("", sum);

复制代码

第一个例子使用了迭代器来计算和,而第二个例子使用了手写循环。在大多数情况下,这两个例子的性能差异并不明显。但是,在某些情况下,手写循环可能会比迭代器更快。单数使用迭代器比使用for循环更简洁,并且保证内存安全

总之,Rust中的迭代器是一种强大的工具,它提供了一种简洁、高效、安全的方式来操作数据。在使用迭代器时,应注意惰性、所有权和借用、迭代器失效和性能问题。from刘金,转载请注明原文链接。感谢!

3.python中map,filter,reduce以及内部实现原理剖析

一.map函数,对任何可迭代序列中的每一个元素应用对应的函数。(不管处理的是什么类型的序列,最后返回的都是列表。)

作用已经在标题中介绍过了,那么先来说说map函数的用法吧。

map(处理逻辑可以是函数也可以是lambda表达式,可迭代的序列)

现在有一个列表。

l1 = [1,2,3,4,5]

现在需要给这个列表里的每一个元素都+1。(当然,使用for循环可以做到对序列中的每个元素进行处理,但使用map函数会更加方便。)

首先,定义一个逻辑函数,要如何对序列中的每一个元素进行处理。

def plus(x):

    return x + 1

#每个元素都+1

print map(plus,l1)

#将map函数应用到l1的每一个元素中。

输出的结果为。

>>>[2, 3, 4, 5, 6]


当然,传入map函数中的逻辑函数还可以使用lambda表达式来表示,还拿刚才的例子演示。

l1 = [1,2,3,4,5]

print map(lambda x:x+1,l1)

>>>[2, 3, 4, 5, 6]


那么map函数是怎么做到的?内部功能是如何实现的?下面就来对map函数的内部进行剖析。

下面这个函数就可以实现和python内置的map函数一模一样的功能。

def map_func(func,seq):

    map_list = []

    for i in seq:

        map_list.append(func(i))

    return map_list


注意!上面说的map函数的使用方法是针对python2.7版本的,针对python3以上的版本不适用,因为从python3开始,map函数不再返回列表,返回的则是一个迭代器,如果想变成列表的话,还需要使用list() 函数做一个转换。



二.filter函数,对序列中的元素进行判断筛选。(返回的结果依旧是个列表。)

fileter函数在拿到一个序列的时候,会开始循环迭代这个序列中的每一个元素,对每一个元素都进行一次if判断,结果为True的加入到列表中,为False的,直接丢掉。

下面是filter函数的简单用法。

一个列表,将这个列表中小于5的元素,全部都列出来。(当然...这么简单的需求也可以使用for循环实现)

filter函数用法和map函数一样都是 filter(函数,可迭代序列)

l1 = [1,2,3,4,5,6,7,8,9,10]


def greater_than(x):

    if x < 5:

        return True

    else:

        return False


print filter(greater_than,l1)

>>>[1, 2, 3, 4]


filter函数依旧可以使用lambda表达式来代替逻辑函数。

print filter(lambda x:x<5,l1)


下面来剖析一下filter函数的工作原理,自己手动写一个和filter功能一模一样的函数:

def filter_func(func,seq):

    filter_list = []

    for s in seq:

        if func(s):

            filter_list.append(s)

    return filter_list


注意啦!这里面的filter函数和map函数一样,用法都是针对python2.7的,在python3中对这个函数做了一些改动,返回的不再是个列表,而是个迭代器。


三.reduce函数,将序列中所有的元素进行合并运算。

现在有个列表,现在需要每个元素要逐个相乘。


l1 = [1,2,3,4,5,6,7,8,9,10]

#reduce函数规定了,一定要传递一个支持一个二元操作的函数

#下面就是定义好的逻辑函数

def bin_func(x,y):

    return x * y


print reduce(plus,l1)

>>> 3628800

下图是reduce函数的工作原理:

技术分享


接下来,解剖下reduce函数的实现代码。

def reduce_func(func,seq,init_num=None):

    if init_num != None:

        ret = init_num

    else:

        ret = seq.pop(0)

    for i in seq:

        ret=func(ret,i)    #如果这条逻辑不好理解的话,可以仔细想想1*2*3*4 = ((1*2)*3)*4 

    return ret


print reduce_func(bin_func,l1)


>>>3628800


reduce函数使用lambda表达式。

print reduce_func(lambda x,y:x*y,l1)


注意!在python3当中,这个reduce函数被放到了functools这个模块中了,需要倒入functools才可以使用这个函数。


最后总结一下:

map函数用于处理序列中每一个元素。

filter函数用于过滤序列中每一个元素。

reduce函数用于做序列的合并操作。

reduce即为化简,它是这样一个过程:每次迭代,将上一次的迭代结果(第一次时为init的元素,如没有init则为seq的第一个元素)与下一个元素一同执行一个二元的func函数。在reduce函数中,init是可选的,如果使用,则作为第一次迭代的第一个元素使用。



本文出自 “reBiRTH” 博客,请务必保留此出处http://suhaozhi.blog.51cto.com/7272298/1907951

以上是关于Rust中的迭代器的使用:map转换filter过滤fold聚合chain链接的主要内容,如果未能解决你的问题,请参考以下文章

map()reduce()filter()总结

map,reduce,filter的总结(reduce还有点不懂,一会自己再看看)

Rust性能分析-迭代器的enumerate方法是否会影响程序性能

3.python中map,filter,reduce以及内部实现原理剖析

map

使用迭代器的最快(最 Pythonic)方式