@@ -1570,6 +1570,163 @@ v.emplace_back(1, "build in place"); // 就地构造
15701570
15711571### 迭代器
15721572
1573+ pass
15731574
1575+ ### 迭代器类型
1576+ *迭代器的本质是什么?*
15741577
1575- #todo
1578+ 省流:迭代器是一个泛型,也是一个 concept. 符合迭代器特征的都是迭代器.
1579+
1580+ 用户很少知道特定迭代器的类型,容器知道自己迭代器的类型,并且以规范定义的 `iterator` 和 `const_iterator` 给用户使用。
1581+
1582+ 有些情况下,迭代器不是成员类型,所以标准库提供了 `iterator_t<X>` 函数来统一接口,对定义的迭代器类型 `X` 可用。
1583+
1584+ #### 流迭代器
1585+
1586+ `istream_iterator<>` `ostream_iterator<>`
1587+
1588+ ### 使用谓词
1589+ *这回真是谓词了,函数式的那种。*
1590+
1591+ ```cpp
1592+ void f(map<string,int>& m) {
1593+ auto p = find_if(m, Greater_than{42}); // 谓词 Greater_than{}.
1594+ }
1595+ ```
1596+
1597+ Greater_than 是函数对象.
1598+
1599+ ``` cpp
1600+ struct Greater_than {
1601+ int val;
1602+ Greater_then(int v) : val{v} {}
1603+ bool operator() (const pair<string,int>& r) const {
1604+ return r.second > val;
1605+ }
1606+ }
1607+ ```
1608+
1609+ 匿名函数也可以.
1610+
1611+ ```cpp
1612+ auto p = find_if(m [](const auto& r) { return r.second > 42 });
1613+ ```
1614+
1615+ ### 标准库算法概览
1616+
1617+ * 懒得全部记完,记一些特殊的.*
1618+
1619+ | name | note |
1620+ | ---------------------------- | ----------------------------------------------- |
1621+ | for_each | 遍历执行 f(x) |
1622+ | find_if | 如果满足 f(x) |
1623+ | count_if | 计数和if |
1624+ | replace_if | |
1625+ | copy_if | |
1626+ | move | |
1627+ | unique_copy | 不拷贝连续重复元素 |
1628+ | sort | 可以多加一个 f(x) 谓词作为排序标准 |
1629+ | ` (p1,p2)=equal_range(b,e,v) ` | ` [pl:p2)是已排序序列[b:e)的子序列,其中元素的值都等于v:本质上等价于二分搜索v ` |
1630+ | merge | 归并序列,并将归并序列存到新序列。可以加 f(x) 作为比较函数 |
1631+
1632+ 每个算法都有 ` <range> ` 版本.
1633+
1634+ 算法会修改元素的值,但是不会添加和删除元素,因为序列并不包含底层容器的信息(* 算法操作的是迭代器,不知道实际用的是什么容器* )。如果要添加删除,得自己手动直接操作容器。
1635+
1636+ 尽可能使用它们编写程序,而不是从头另起炉灶。
1637+
1638+ ### 并行算法
1639+
1640+ 有两种执行方式:
1641+
1642+ - 并行执行:任务在多线程中完成。
1643+ - 数组化执行(向量化执行):在单线程用 SIMD 完成。
1644+
1645+ ` <execution> ` 中有命名空间 execution,会有如下参数:
1646+
1647+ - seq 顺序执行
1648+ - par 如果可能,则并行执行
1649+ - unseq 非顺序(数组化) 执行 如果可能
1650+ - par_unseq 并行执行和数组化执行 如果可能
1651+
1652+ 考虑根据硬件使用并行算法。
1653+
1654+ ``` cpp
1655+ sort (par_unseq, v.begin(), v.end());
1656+ ```
1657+
1658+ ### 建议
1659+
1660+ - 搜索的时候,通常返回输入序列的末尾位置来表示未找到。
1661+ - 使用 using 类型别名清理杂乱的符号。 *当然不是让你在全局作用域用 using。*
1662+
1663+ ## 第14章 范围
1664+ *range 实际上也是概念.*
1665+
1666+ range 有如下定义方式:
1667+
1668+ - 一对 bgein end 迭代器
1669+ - 一对 bgein n,n是元素个数
1670+ - 一对 bgein pred,pred 是谓词,如果 `pred(p)` 为真,则表示达到了范围末端。
1671+
1672+ ### 视图
1673+
1674+ > 视图是查看范围的一种方式.
1675+
1676+ ```cpp
1677+ filter_view v {r, [](int x) { return x%2; }}; // 查看 r 中的奇数
1678+ for (int x : v)
1679+ cout << x << ' ';
1680+ ```
1681+
1682+ 类似还有 ` take_view ` ,也就是拿前几个。
1683+
1684+ 可以不写名字直接玩视图嵌套。 * 函数式管道:初现端倪*
1685+
1686+ ``` cpp
1687+ for (int x : take_view{ filter_view {r, [ ] (int x) { return x%2; } } , 3})
1688+ cout << x << ' ';
1689+ ```
1690+
1691+ ![[Pasted image 20251110153452.png]]
1692+
1693+ 不抄了,自己看。
1694+
1695+ 视图和范围很相似,但区别在于 **视图不拥有元素本身**,释放范围的元素责任在范围身上。所以视图的生命周期不能大于范围的生命周期。
1696+
1697+ 视图复制开小很低,可以用值传递。
1698+
1699+ ### 生成器
1700+ *范围工厂.*
1701+
1702+ ![[Pasted image 20251110154016.png]]
1703+
1704+ *itoa_view 就类似 python 的 range 了,可以生成数字序列。*
1705+
1706+ ### 管道
1707+
1708+ ```cpp
1709+ for (int x : r | views::filter(odd) | views::take(3)) // odd 是谓词
1710+ cout << x << ' ';
1711+ ```
1712+
1713+ 管道从左至右执行。
1714+
1715+ 这些过滤器函数定义在 ` ranges::views ` 中.
1716+
1717+ > 视图和管道的实现涉及一些令人毛骨悚然的模板元编程,如果你对性能表示担忧,请确保先对你实现的性能进行测量,确定其是否符合需求。如果不符合,可以用传统的替代方案来实现。
1718+
1719+ * 我听劝,我不看怎么实现的了。*
1720+
1721+ ### 概念概述
1722+ * 怎么跑这章来了.*
1723+
1724+ ![ [ Pasted image 20251110154632.png]]
1725+
1726+ * 多少有点过于魔法了,用到我再记吧.*
1727+
1728+ ### 建议
1729+
1730+ - 如果迭代器对的样子变得冗长,用范围版本算法
1731+ - 理想的类型应满足 relugar 概念。
1732+ - 尽可能使用标准库的概念。
0 commit comments