现代 C++20 实战高手课
卢誉声
Autodesk 首席工程师
3781 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 29 讲
现代 C++20 实战高手课
15
15
1.0x
00:00/00:00
登录|注册

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
立即购买
登录 后留言

全部留言(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归属地:北京
    2
    1
  • 常振华
    还是更喜欢传统的方式,可以用不修改原来变量的方式去实现多线程处理
    2023-10-19归属地:广东
收起评论
显示
设置
留言
3
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部