#comprehension #macro #python #vector

rustcomp

为 Rust 添加向量、映射、集合和迭代器推导

5 个版本 (3 个破坏性更新)

0.4.0 2023 年 10 月 4 日
0.3.1 2023 年 10 月 4 日
0.3.0 2023 年 10 月 4 日
0.2.0 2023 年 10 月 3 日
0.1.0 2023 年 10 月 2 日

#916Rust 模式

每月 26 次下载

MIT 协议

17KB
78

rustcomp

crates.io docs

为 Rust 添加惯用推导。这是通过一个函数宏 rcomp! 实现的,该宏展开为迭代器。

以下内容修改自 文档

基本用法

核心思想很简单:提供一个简单且简洁的方式来扁平化、过滤、映射和收集迭代器。有关语法的完整分解,请参阅 rcomp! 宏的文档。现在,考虑以下简单示例

let v = rcomp![Vec<_>; for x in 0..10 => x];
let it = (0..10).collect::<Vec<_>>(); // all examples show an equivalent iterator

这将创建一个包含数字 0 到 9 的 Vec<i32> 的向量...不是很实用,对吧?让我们添加一个守卫来过滤掉奇数

let v = rcomp![Vec<_>; for x in 0..10 => x, if x % 2 == 0];
let it = (0..10).filter(|x| x % 2 == 0).collect::<Vec<_>>();

现在我们有点进展了!您还可以映射值,所以让我们将它们加倍以供娱乐

let v = rcomp![Vec<_>; for x in 0..10 => x * 2, if x % 2 == 0];
let it = (0..10)
    .filter(|x| x % 2 == 0)
    .map(|x| x * 2)
    .collect::<Vec<_>>();

注意,在迭代器示例中,map 调用是在 filter 调用之后。这正是推导的工作方式:守卫应用于 输入 值,而不是输出值。

说到迭代器,如果您不想将结果收集到容器中,可以直接通过省略收集类型来获取迭代器

// now we have to collect the iterator ourselves
let v = rcomp![for x in 0..10 => x].collect::<Vec<_>>();
// equivalent to:
let vv = rcomp![Vec<_>; for x in 0..10 => x];

解构

推导也支持解构。例如,元组

let pairs = vec![(1, 2), (3, 4), (5, 6)];
let v = rcomp![Vec<_>; for (x, y) in &pairs => x + y];
let it = pairs.into_iter().map(|(x, y)| x + y).collect::<Vec<_>>();

或结构体

struct Point {
  x: i32,
  y: i32,
}

let points = vec![Point::new(1, 2), Point::new(3, 4), Point::new(5, 6)];
let v = rcomp![Vec<_>; for Point { x, y } in &points => x + y];
let it = points.into_iter().map(|Point { x, y }| x + y).collect::<Vec<_>>();

扁平化

通过链式连接 for-in 子句,支持递归限制内的嵌套迭代器的扁平化。

let matrix = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
let v = rcomp![Vec<_>; for row in &matrix, col in row => *col * 2, if *col % 2 == 0];
// nested loops are a much nicer example than iterators here
let mut it = Vec::new();
for row in &matrix {
    for col in row {
        if *col % 2 == 0 {
           it.push(*col * 2);
       }
    }
}

高级示例

有关创建 HashMapHashSet 等的高级示例,请参阅 rcomp! 宏的文档。

迭代器示例说明

需要注意的是,用于测试推导的迭代器示例与推导是 等效的,但 不是相同的。宏展开为嵌套的 flat_mapfilter_map 调用链;示例是为了清晰度和显示推导中的操作顺序而编写的。例如,早期的矩阵示例展开为

let v = (&matrix)
    .into_iter()
    .flat_map(|row| {
        row.into_iter().filter_map(|col| {
            if (*col % 2 == 0) && true {
                Some((*col * 2))
            } else {
                None
            }
        })
    })
    .collect::<Vec<_>>();

注意展开中使用了 into_iter

那么 mapcomp 呢?

我知道存在mapcomp crate,但它在几个方面与这个crate有所不同。首先,mapcomp旨在使其语法尽可能接近Python,我认为他们做得很好;这个crate并不试图这样做。这个crate的目标是以一种符合Rust语法的自然方式向Rust添加列表推导,同时保持其简洁和强大。此外,mapcomp提供了多个宏来支持不同类型的列表推导,而此crate只提供了一个。

在更技术性的层面上,mapcomp内部使用生成器,这在Rust 2018版本中是可以的,但现在生成器和yield都是实验性特性。这为此crate提供了很大的启发,因为我想要创建一个基于宏的解决方案,不需要nightly版本,所以我选择了迭代器而不是生成器。

无运行时依赖