13|Ranges实战:数据序列函数式编程
卢誉声
你好,我是卢誉声。
通过前面的学习,我们已经了解到,C++ Ranges 作为基础编程工具,可以大幅加强函数式编程的代码可读性和可维护性,解决了 C++ 传统函数式编程的困境。在 C++20 的加持下,我们终于可以优雅地处理大规模数据了。
在讲解完 Ranges 的概念和用法后,我们还是有必要通过实战来融会贯通 C++ Ranges。它的用法比较灵活,在熟练使用后,我相信你会在今后的代码实现中对它爱不释手。
在处理规模型数据时,函数式编程特别有用。为了让你建立更直观的感受,今天我为你准备了一个实战案例,设计一个简单的统计分析程序,用来分析三维视图中的对象。
模块设计
在这个实战案例里,我们主要是展示 Ranges 的强大功能,而非数据本身的严谨性和正确性。因此,你可以重点关注处理数据的部分。
那么,要分析统计的数据长什么样子呢?我们假设一个三维模型包含多个视图,每个视图包含一定量的三维对象。某个三维对象中的三角面片就组成了逻辑上的三维对象。同时,三维模型会将视图分成高精度视图和低精度视图。
我造了一份简单的数据,一个三维模型的统计分析表是后面这样。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
C++ Ranges在函数式编程中的强大功能是本文的重点。通过实战案例展示了Ranges库在数据序列处理中的应用。文章详细介绍了模块设计和关键数据类型的定义,强调了利用Ranges库实现统计分析功能的重要性。对比传统STL函数式实现,突出了基于Ranges的函数式实现的优势,以及在并行化处理问题上的效率。此外,文章还介绍了操作符重载实现视图管道支持的技巧,以及创建“仿range适配器闭包”来模拟range适配器闭包对象的方式。总的来说,Ranges库可以大幅提高C++中函数式编程的代码可读性,降低代码复杂度,提高函数式编程效率。读者通过本文可以更好地理解和应用C++ Ranges在函数式编程中的优势。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《现代 C++20 实战高手课》,新⼈⾸单¥59
《现代 C++20 实战高手课》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(3)
- 最新
- 精选
- 李云龙按照老师上次留言给出的指导意见,已修改代码,这里给出关键代码: 协程类修改成下面的代码,注意 final_suspend 的返回值需要修改成suspend_always,否则在我的这个使用场景中,协程退出时会抛异常: export struct Coroutine { struct promise_type { std::string _value; Coroutine get_return_object() { return { ._handle = std::coroutine_handle<promise_type>::from_promise(*this) }; } std::suspend_never initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } std::suspend_always yield_value(std::string value) { _value = RemoveNumber(value); return {}; } void return_void() {} void unhandled_exception() {} }; std::coroutine_handle<promise_type> _handle; }; 字符串的处理: export string RemoveNumber(string& input) { return input | views::filter([](char ch) { return !isdigit(ch); }) | to<string>(); } main.cpp中的调用: Coroutine asyncStr() { string input; while (getline(cin, input)) { if (input == "End") { break; } co_yield input; } } int main() { auto h = asyncStr()._handle; auto& promise = h.promise(); while (!h.done()) { cout << "处理结果:" << promise._value << endl; h(); } h.destroy(); return 0; }
作者回复: 点赞
2024-01-27归属地:北京1 - 李云龙分享一下我的思考题的答案,我这里只给出关键代码: export module asyncString.stringHandle:handler; import asyncString.utils; import asyncString.task; import <ctype.h>; import <ranges>; import <algorithm>; import <string>; import <sstream>; import <vector>; import <numeric>; using std::vector; using std::string; using std::istringstream; namespace asyncString::stringHandle { namespace views = std::ranges::views; namespace ranges = std::ranges; using asyncString::utils::views::to; using asyncString::task::asyncify; export vector<string> RemoveNumber(vector<string>& vecInput) { return vecInput | views::transform( [](string& str) { istringstream iss{ str }; ranges::istream_view<string> isView{ iss }; auto resultStr = isView | views::transform([](string word) { return word | views::filter([](char ch) { return !isdigit(ch); }) | to<string>(); }) | to<vector<string>>(); string iniStr; string joinStr = std::accumulate(resultStr.begin(), resultStr.end(), iniStr, [](string prev, string& val) { return prev + " " + val; }); return joinStr; } ) | to<vector<string>>(); } export auto RemoveNumberAwaiter(vector<string>& inputVec) { return asyncify([&inputVec]() { return RemoveNumber(inputVec); }); } } 完整代码,请参阅gitee仓库:https://gitee.com/devin21/rangeAssignment/tree/master
作者回复: 这个基本代码思路没有问题 。 但是有两个细节需要注意: 1.过滤数字后没必要先转换成字符串再合并,应该直接合并,否则效率比较低,而且字符之间拼接不应该包含空格 2.Coroutines的使用有点生硬,其实可以有两个地方采用: (1)如果输入字符串是来自于用户输入,那么可以在用户输入这里做I/O异步。 (2)如果输入字符串是函数的参数并且数量极大,可以借助高性能异步队列在处理计算任务。 核心是不必要时让出CPU,避免主线程一直阻塞。
2024-01-14归属地:北京21 - 常振华还是更喜欢传统的方式,可以用不修改原来变量的方式去实现多线程处理2023-10-19归属地:广东
收起评论