c++でも畳み込みたい!

よくやる処理に、ある配列またはコンテナに入っている要素の総和(マージ?結合?)したいというものがあるので、 簡潔に書ける方法はないかと調べたところ、STLにはstd::accumulate()があるらしい。
accumulate(畳み込み)という操作は既にHaskellを学ぶ中で知っていたため概念としてはすぐに理解できたものの、 色々ハマりやすそうなのでテストコードをメモっておく。

#include <iostream>
#include <vector>
#include <numeric>
#include <string>

using namespace std;

class myClass {
public:
    uint32_t _num;
    std::string _str;

    myClass(const uint32_t num, const std::string str){
        _num = num;
        _str = str;
    }

    // `+`演算子オーバーロード
    myClass operator+(const myClass &y) const {
        return myClass(
            this->_num + y._num, this->_str + "-" + y._str);
    }
};

// BinaryOperator
class myMerge {
public:
    myClass operator()(const myClass &x, const myClass &y) const {
        return myClass(
            x._num * y._num, x._str + "," + y._str);
    }
};

int main()
{
    std::vector<string> list = {"hoge", "fuga", "foo", "bar"};

    // 初期値をvectorの第一要素とした場合はbegin()+1を開始要素とする
    string hogefuga = std::accumulate(list.begin() + 1, list.end(), list[0]);
    std::cout << "hogefuga:" << hogefuga << std::endl;
    // -> hogefuga:hogefugafoobar

    // 初期値を定数にしてもよいならbegin()を開始要素にするとわかりやすいかもしれない
    string hoge = std::accumulate(list.begin(), list.end(), string(""));
    std::cout << "hoge    :" << hogefuga << std::endl;
    // -> hoge    :hogefugafoobar

    // この書き方をするとコンテナの第一要素が重複してしまうので注意
    string hogehoge = std::accumulate(list.begin(), list.end(), list[0]);
    std::cout << "hogehoge:" << hogehoge << std::endl;
    // -> hogehoge:hogehogefugafoobar

    // 要素が1つしかない場合でも動く
    std::vector<string> lonely = {"alone"};
    string sad = accumulate(lonely.begin() + 1, lonely.end(), lonely[0]);
    std::cout << "home " << sad << std::endl;
    // -> home alone

    // コンテナが空になる可能性があるなら2番目の書き方はできない(SEGVが発生する)
    std::vector<string> empty = {};
    std::cout << "からっぽ:" << std::accumulate(
                        empty.begin(), empty.end(), string("")) << std::endl;
    // -> からっぽ:
    // std::cout << "からっぽ:" << std::accumulate(empty.begin() + 1, empty.end(), empty[0]) << std::endl;
    // -> SEGV

    // 自前クラスに対するaccumulate
    std::vector<myClass> myList = { {2, "A"}, {3, "B"}, {4, "C"}};

    // `+`演算子オーバーロード
    myClass myAcc = std::accumulate(myList.begin() + 1, myList.end(), myList[0]);
    std::cout << "myAcc.num = " << myAcc._num << " myAcc.str:" << myAcc._str << std::endl;
    // -> myAcc.num = 9 myAcc.str:A-B-C

    // 関数オブジェクト(BinaryOperator)指定
    myClass myAcc2 = std::accumulate(myList.begin() + 1, myList.end(), myList[0], myMerge());
    std::cout << "myAcc2.num = " << myAcc2._num << " myAcc.str:" << myAcc2._str << std::endl;
    // -> myAcc2.num = 24 myAcc.str:A,B,C
}

参考文献