3个不稳定版本
0.2.0 | 2023年5月31日 |
---|---|
0.1.1 | 2023年3月17日 |
0.1.0 | 2023年3月16日 |
2995 在 Rust模式
每月下载量 36
8KB
81 行
Chain tools: Rust本应如此
chain_tools 是一个库,旨在将每个函数都转换为一条长链式函数。灵感来源于学习Haskell,并发现自己对列表解析的限制感到沮丧。
关于列表解析的一番不算简短的牢骚
对于那些可能不知道的人来说,列表解析旨在成为一种快速定义基于某些其他列表的新列表的方法。例如,在Python中,你可以写 [x*x for x in range(10) if x % 2 == 0]
。这将返回小于10的每个偶数的平方。在Haskell中,这将是 [x^2 | x <- [0..9], even x]
。起初,这种语法可能看起来很复杂,特别是在Haskell中,单词被符号替换,但很快就会变得容易阅读。问题是混淆。
一个特性让人困惑并不是问题。毕竟,我爱Rust,这是一种许多人认为最难学和最让人困惑的语言。这些情感并不错。开始学习Rust时,你学到的每个新功能都伴随着十层新的复杂性。这种复杂性可能会让语言对初学者来说更难,但它存在是有原因的。一旦你理解了这些复杂的系统,它们给予你的力量是无与伦比的。Rust的类型系统很复杂,但它也是任何语言的最佳功能。
明确了这一点之后,让我们回到列表推导式。Rust没有列表推导式。Rust不需要列表推导式。Rust有更好的东西。当面对给开发者提供一种简单的方式来转换列表的任务时,Python和Haskell遇到了问题。它们的语言并没有围绕这种任务来设计。在Python中,你甚至不能在某个函数(如len(list)
)的包装下找到列表的长度。添加这种功能实际上需要全新的语法。Haskell决定将整个语言围绕列表推导式来构建。我要给他们一些赞誉,它确实看起来非常“数学化”。
当Rust面临这个相同的问题时,许多前期工作已经完成。迭代器特性已经可以通过.filter()
和.map()
方法来扩展。如果你将它们结合起来,你将得到.filter_map()
函数,它做的是与列表推导式完全相同的事情。功能是相同的。以下是一些Rust代码示例来演示:(0..10).filter_map(|x| (x % 2 == 0).then(|| x * x))
。你还可以单独使用这两个方法,如下所示:(0..10).filter(|x| x % 2 == 0).map(|x| x * x)
。
那么它的意义是什么呢?我可以看到很多人更喜欢列表推导式的样式,而不是Rust的过滤和映射。我甚至不能说他们是错的,毕竟外观是主观的,但有一些真理需要牢记。
真理#1: 列表推导式打乱了顺序。
大多数编程语言按照以下顺序操作:从上到下,从左到右。列表推导式打乱了第二个顺序。首先得到输出,然后是定义,然后是源,然后是条件。《这是》《从》《这个》《当》《这个》时。《这可能不是什么大问题,但代码本身可能已经很难阅读,而打乱顺序只会使阅读更加困难》。
真理#2: 列表推导式不灵活。
列表推导只能做四件事。筛选、映射、筛选-映射或复制。如果你想先映射再筛选呢?如果想链接着另一个列表呢?如果想折叠值呢?你可以做这些事,当然可以,但可能需要将多个推导语句链在一起,或者完全放弃使用。这个旨在让生活更美好的功能,由于它的不可灵活性,在许多情况下都变得毫无用处。
这正是Rust的亮点所在。能够一个接一个地链式调用迭代器函数调用,这提供了真正的灵活性。先筛选后映射?iter.filter().map()
。先映射后筛选?iter.map().filter()
。另一个筛选和映射?
iter
.filter()
.map()
.filter()
.inspect()
.cycle()
.skip(5)
.filter_map()
...
这可以持续到你需要的程度。关键就在这里。无论何时你需要,这个系统都足够灵活,可以完成你想要的任务,并在执行过程中保持可读性。无论链有多长,序列始终不变。逻辑从函数到函数清晰易懂,从不中断。
- 待办事项:添加更多抱怨。