ORNEW

C++で畳み込み

Share on Facebook
Pocket

この記事は2015年の記事を移植したものです。


C++で畳み込み関数を実装した。

template< typename F, typename T, typename U >
auto fold_left( F f, T a, U b )
{
  return f( a, b );
}
template< typename F, typename T, typename U, typename... V >
auto fold_left( F f, T a, U b, V... v )
{
  return fold_left( f, f( a, b ), v... );
}

使用例

auto catenate( std::string a, std::string b )
{
  return a + b;
}
struct catenate_predicate
{
  template< typename T, typename U >
  auto operator()( T a, U b )
  {
    return catenate( a, b );
  }
};

std::string a{ "aaaaaa" };
std::string b{ "bbbbbb" };
std::string c{ "cccccc" };
std::cout << fold_left(catenate_predicate{}, a, b, c );
// output: aaaaaabbbbbbcccccc

このコードは叙述関数を定義している。完全ではない。C++14に導入されたジェネリックラムダで解決できる。

std::string a{ "aaaaaa" };
std::string b{ "bbbbbb" };
std::string c{ "cccccc" };
std::cout
  << fold_left([]( auto a, auto b ){ return catenate( a, b ); }, a, b, c );
// output: aaaaaabbbbbbcccccc

美しい!

もちろん今までのC++で似たようなことをする場合と比較した相対的な意味であるが、十分感動できる。

C++17になれば可変長引数のfold式が導入されるが、今のところ演算子での畳込みしかできない。

大したコードではないので是非コピペして皆使い倒して欲しいと思う。あと気力があれば完全転送やconst対応もするといい。

最後にfold_rightも載せたテストコード全体を載せておく。

#include <iostream>
#include <string>
namespace notmoon
{
namespace algorithm
{
  template< typename F, typename T, typename U >
  auto fold_left( F f, T a, U b )
  {
    return f( a, b );
  }
  template< typename F, typename T, typename U, typename... V >
  auto fold_left( F f, T a, U b, V... v )
  {
    return fold_left( f, f( a, b ), v... );
  }
  template< typename F, typename T, typename U >
  auto fold_right( F f, T a, U b )
  {
    return f( a, b );
  }
  template< typename F, typename T, typename U, typename... V >
  auto fold_right( F f, T a, U b, V... v )
  {
    return f( a, fold_right( f, b, v... ) );
  }

  template< typename T, typename U >
  auto catenate( T, U );
  auto catenate( std::string a, std::string b )
  {
    return a + b;
  }
  struct catenate_predicate
  {
    template< typename T, typename U >
    auto operator()( T a, U b )
    {
      return catenate( a, b );
    }
  };
}
}
int main()
{
  using namespace notmoon::algorithm;
  std::string a{ "aaaaaa" };
  std::string b{ "bbbbbb" };
  std::string c{ "cccccc" };
  std::cout
    << catenate( a, b ) << std::endl
    << fold_left(catenate_predicate{}, a, b, c ) << std::endl
    << fold_right(catenate_predicate{}, a, b, c ) << std::endl
    << fold_left([]( auto a, auto b ){ return catenate( a, b ); }, a, b, c ) << std::endl;
}

PF版

template< typename F, typename T, typename U >
auto fold_left( F&& f, T&& a, U&& b )
{
  return f( std::forward<T>(a), std::forward<U>(b) );
}
template< typename F, typename T, typename U, typename... V >
auto fold_left( F&& f, T&& a, U&& b, V&&... v )
{
  return fold_left(
      std::forward<F>(f),
      f( std::forward<T>(a), std::forward<U>(b) ),
      std::forward<V>(v)... );
}