一个函数式编程中常见的右折叠(fold_right) 操作。在C++中可以通过多种方式实现,我将为你提供几种不同风格的实现方法。
📋 核心概念对比
| 特性 | MoonBit示例 | C++对应概念 |
|---|---|---|
| 递归结构 | List[A] (Cons/Nil) |
递归数据结构或范围(Range) |
| 右折叠 | fold_right |
从右向左处理元素 |
| 类型参数 | [A, B] |
模板参数 T, U |
🛠️ C++实现方案
方案1:使用递归和自定义链表(最贴近原版)
cpp
#include <iostream>
#include <functional>
#include <memory>
// 1. 定义函数式风格的链表
template<typename A>
struct List {
struct Node {
A head;
std::shared_ptr<Node> tail;
Node(A h, std::shared_ptr<Node> t = nullptr) : head(h), tail(t) {}
};
std::shared_ptr<Node> root = nullptr;
List() = default;
List(A h, List t) : root(std::make_shared<Node>(h, t.root)) {}
bool empty() const { return !root; }
A head() const { return root->head; }
List tail() const {
List result;
result.root = root ? root->tail : nullptr;
return result;
}
// 方便构建的静态方法
static List cons(A h, List t) { return List(h, t); }
static List nil() { return List(); }
};
// 2. 实现 fold_right(递归版本,最贴近MoonBit)
template<typename A, typename B>
B fold_right(const List<A>& list,
std::function<B(A, B)> f,
B initial) {
if (list.empty()) {
return initial;
} else {
// f(hd, fold_right(tl, f, b))
return f(list.head(),
fold_right(list.tail(), f, initial));
}
}
// 使用示例
int main() {
// 创建链表: 1 -> 2 -> 3 -> nil
auto list = List<int>::cons(1,
List<int>::cons(2,
List<int>::cons(3,
List<int>::nil())));
// 求和(右折叠)
auto sum = fold_right<int, int>(list,
[](int hd, int acc) { return hd + acc; },
0);
std::cout << "Sum: " << sum << std::endl; // 6
// 连接字符串
auto str_list = List<std::string>::cons("a",
List<std::string>::cons("b",
List<std::string>::cons("c",
List<std::string>::nil())));
auto concat = fold_right<std::string, std::string>(str_list,
[](const std::string& hd, const std::string& acc) {
return hd + acc;
},
"");
std::cout << "Concatenated: " << concat << std::endl; // "abc"
return 0;
}
方案2:使用标准库容器和反向迭代器
cpp
#include <iostream>
#include <vector>
#include <list>
#include <functional>
#include <ranges>
// 使用反向迭代器模拟 fold_right
template<typename Container, typename Func, typename B>
B fold_right(const Container& container, Func f, B initial) {
// 从右向左处理
auto current = initial;
// 使用反向迭代器
for (auto it = container.rbegin(); it != container.rend(); ++it) {
current = f(*it, current);
}
return current;
}
// 递归版本(更贴近函数式风格)
template<typename Container, typename Func, typename B>
B fold_right_recursive(typename Container::const_reverse_iterator it,
typename Container::const_reverse_iterator end,
Func f, B acc) {
if (it == end) {
return acc;
}
auto current = *it;
++it;
return f(current, fold_right_recursive(it, end, f, acc));
}
template<typename Container, typename Func, typename B>
B fold_right_recursive_wrapper(const Container& c, Func f, B initial) {
return fold_right_recursive(c.rbegin(), c.rend(), f, initial);
}
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 1. 迭代器版本
auto sum = fold_right(vec,
[](int x, int acc) { return x + acc; },
0);
std::cout << "Sum (iterative): " << sum << std::endl; // 15
// 2. 递归版本
auto product = fold_right_recursive_wrapper(vec,
[](int x, int acc) { return x * acc; },
1);
std::cout << "Product (recursive): " << product << std::endl; // 120
// 3. 字符串操作
std::list<std::string> words = {"Hello", " ", "World", "!"};
auto sentence = fold_right(words,
[](const std::string& s, const std::string& acc) {
return s + acc;
},
std::string(""));
std::cout << "Sentence: " << sentence << std::endl; // "Hello World!"
return 0;
}
方案3:C++23 Ranges风格(最现代)
cpp
#include <iostream>
#include <vector>
#include <ranges>
#include <functional>
#include <algorithm>
// C++23 风格:结合 ranges::fold_right(如果编译器支持)
// 或者自己实现一个类似的
template<std::ranges::input_range R, typename T, typename F>
T fold_right_range(R&& rng, T init, F f) {
// 先将范围转换为向量以便反向访问
std::vector<std::ranges::range_value_t<R>> temp;
std::ranges::copy(rng, std::back_inserter(temp));
// 从右向左折叠
return std::accumulate(
temp.rbegin(), temp.rend(), init, f
);
}
// 递归模板元编程版本(编译时计算)
template<typename... Args>
struct FoldRightHelper;
template<typename T>
struct FoldRightHelper<T> {
template<typename Func, typename Init>
static auto apply(Func&&, Init&& init) {
return init;
}
};
template<typename Head, typename... Tail>
struct FoldRightHelper<Head, Tail...> {
template<typename Func, typename Init>
static auto apply(Func&& f, Init&& init, Head head, Tail... tail) {
return f(head, FoldRightHelper<Tail...>::apply(
std::forward<Func>(f),
std::forward<Init>(init),
tail...));
}
};
template<typename Func, typename Init, typename... Args>
auto fold_right_constexpr(Func&& f, Init&& init, Args... args) {
return FoldRightHelper<Args...>::apply(
std::forward<Func>(f),
std::forward<Init>(init),
args...);
}
int main() {
// 1. Ranges版本
std::vector<int> nums = {1, 2, 3, 4};
auto result = fold_right_range(nums, 0,
[](int x, int acc) { return x - acc; });
std::cout << "Right fold subtraction: " << result << std::endl; // 1 - (2 - (3 - (4 - 0))) = -2
// 2. 编译时计算版本
constexpr auto constexpr_result = fold_right_constexpr(
[](int x, int acc) { return x + acc; },
0, 1, 2, 3, 4, 5);
std::cout << "Compile-time sum: " << constexpr_result << std::endl; // 15
// 3. 演示右折叠的特性
std::vector<std::string> ops = {"a", "b", "c"};
auto right_assoc = fold_right_range(ops, std::string("z"),
[](const std::string& x, const std::string& acc) {
return "(" + x + " ⊕ " + acc + ")";
});
std::cout << "Right associative: " << right_assoc << std::endl;
// 输出: (a ⊕ (b ⊕ (c ⊕ z)))
return 0;
}
🎯 关键点解析
1. 右折叠与左折叠的区别
cpp
// 左折叠:((((0 + 1) + 2) + 3) + 4)
auto left_fold = std::accumulate(vec.begin(), vec.end(), 0, std::plus<>{});
// 右折叠:(1 + (2 + (3 + (4 + 0))))
// 对于加法结果相同,但对于非结合性操作不同
auto right_fold = fold_right(vec, std::plus<>{}, 0);
2. 递归深度问题
右折叠的递归实现可能导致栈溢出,对于长列表应该:
- 使用迭代器版本
- 或使用尾递归优化(如果编译器支持)
- 或使用C++23的
std::ranges::fold_right
3. 性能考虑
cpp
// 高效方案:对于向量,使用反向迭代器
template<typename T, typename Func, typename B>
B efficient_fold_right(const std::vector<T>& vec, Func f, B init) {
B result = init;
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
result = f(*it, result);
}
return result;
}
📊 方案选择指南
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 学习函数式编程概念 | 方案1(自定义链表) | 最贴近MoonBit原版 |
| 实际项目,使用标准容器 | 方案2(反向迭代器) | 性能好,代码简洁 |
| C++23新项目 | 方案3(Ranges风格) | 现代,表达力强 |
| 编译时计算 | 方案3(模板元编程) | 零运行时开销 |
💡 进阶:通用实现
cpp
// 通用的右折叠实现,支持任何双向范围
template<typename Range, typename Func, typename Init>
auto generic_fold_right(Range&& range, Func f, Init init) {
using std::ranges::rbegin, std::ranges::rend;
auto result = init;
for (auto it = rbegin(range); it != rend(range); ++it) {
result = f(*it, std::move(result));
}
return result;
}
// 支持管道操作符的版本
template<typename Func, typename Init>
auto make_fold_right(Func f, Init init) {
return [f = std::move(f), init = std::move(init)]
(auto&& range) mutable {
return generic_fold_right(
std::forward<decltype(range)>(range),
std::move(f),
std::move(init)
);
};
}
// 使用示例
int main() {
std::vector<int> v = {1, 2, 3};
// 直接使用
auto sum = generic_fold_right(v, std::plus<>{}, 0);
// 管道风格
auto result = v | std::views::filter([](int x) { return x > 1; })
| std::views::transform([](int x) { return x * 2; })
| make_fold_right(std::plus<>{}, 0);
return 0;
}
建议:如果你想要最贴近MoonBit语义的实现,使用方案1的递归版本。如果注重性能和生产环境使用,方案2的反向迭代器版本是最佳选择。
如果你需要实现特定类型的折叠(如处理树结构、并行折叠等),我可以提供更专门的实现。