C++实现右折叠

一个函数式编程中常见的右折叠(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的反向迭代器版本是最佳选择。

如果你需要实现特定类型的折叠(如处理树结构、并行折叠等),我可以提供更专门的实现。

相关推荐
豐儀麟阁贵6 小时前
9.5格式化字符串
java·开发语言·前端·面试
handsomezqh6 小时前
洛谷U611548 助教的比拼
c++·算法
小李小李快乐不已6 小时前
图论理论基础(4)
c++·算法·图论·迭代加深
崇山峻岭之间6 小时前
C++ Prime Plus 学习笔记025
c++·笔记·学习
m0_740043736 小时前
Axios拦截器 -- 请求拦截器和响应拦截器
开发语言·前端·javascript
bkspiderx6 小时前
C++操作符优先级与结合性全解析
c++·思维导图·操作符优先级·结合性
楼田莉子6 小时前
基于Linux的个人制作的文件库+标准输出和标准错误
linux·c语言·c++·学习·vim
程序修理员6 小时前
java+vue实现文件下载进度条
java·开发语言·vue.js
梁正雄7 小时前
10、Python面向对象编程-2
开发语言·python