组合子 (Combinators)
迭代器能做的远不止 for 循环!
查看 Iterator 特质的文档,你会发现一个庞大的方法集合,可用于以各种方式变换、过滤、组合迭代器。
我们说说最常见的几个:
map:对迭代器的每个元素应用一个函数。filter:只保留满足某谓词 (predicate) 的元素。filter_map:一步完成filter和map。cloned:把对引用的迭代器转换为值的迭代器,对每个元素进行克隆 (clone)。enumerate:返回一个产生(index, value)对的新迭代器。skip:跳过迭代器的前n个元素。take:在n个元素后停止迭代器。chain:把两个迭代器合为一个。
这些方法被称作组合子 (combinator)。
通常链式 (chained) 调用,从而以简洁可读的方式构造复杂变换:
let numbers = vec![1, 2, 3, 4, 5];
// 偶数平方和
let outcome: u32 = numbers.iter()
.filter(|&n| n % 2 == 0)
.map(|&n| n * n)
.sum();
闭包 (Closures)
上面的 filter 和 map 方法在做什么?
它们以闭包 (closure) 作为参数。
闭包是匿名函数 (anonymous function),即不通过我们熟悉的 fn 语法定义的函数。
它们用 |args| body 语法定义,args 是参数,body 是函数体。
body 可以是一段代码块或单个表达式。
例如:
// 一个匿名函数,把参数加 1
let add_one = |x| x + 1;
// 也可以用代码块写:
let add_one = |x| { x + 1 };
闭包可以接收多个参数:
let add = |x, y| x + y;
let sum = add(1, 2);
它们也能从环境中捕获 (capture) 变量:
let x = 42;
let add_x = |y| x + y;
let sum = add_x(1);
如有需要,可以指定参数和/或返回类型:
// 只标注输入类型
let add_one = |x: i32| x + 1;
// 也可以使用 `fn` 语法标注输入和输出
let add_one: fn(i32) -> i32 = |x| x + 1;
collect
用组合子变换完一个迭代器之后,要做什么?
你要么用 for 循环遍历变换后的值,要么把它们收集到一个集合里。
后者是用 collect 方法完成。
collect 消费迭代器,把它的元素收集到你选择的集合里。
例如,可以把偶数的平方收集到 Vec 中:
let numbers = vec![1, 2, 3, 4, 5];
let squares_of_evens: Vec<u32> = numbers.iter()
.filter(|&n| n % 2 == 0)
.map(|&n| n * n)
.collect();
collect 对其返回类型 (return type) 是泛型的。
所以你通常需要给出类型提示来帮编译器推断正确类型。
上面的例子中,我们把 squares_of_evens 的类型标注为 Vec<u32>。
或者你也可以用鱼叉语法 (turbofish syntax) 来指定类型:
let squares_of_evens = numbers.iter()
.filter(|&n| n % 2 == 0)
.map(|&n| n * n)
// 鱼叉语法:`<方法名>::<类型>()`
// 之所以叫 turbofish,是因为 `::<>` 看起来像一条鱼
.collect::<Vec<u32>>();
进一步阅读
Iterator的文档 概览了std中迭代器可用的方法。itertoolscrate 为迭代器定义了更多组合子。
原文链接:英文原文