Skip to content

Instantly share code, notes, and snippets.

@tomoyanonymous
Last active April 1, 2021 06:18
Show Gist options
  • Save tomoyanonymous/ba74f315e8153b14d35e6e60fc2bad6f to your computer and use it in GitHub Desktop.
Save tomoyanonymous/ba74f315e8153b14d35e6e60fc2bad6f to your computer and use it in GitHub Desktop.
Do functional map (iterate functions over different kind of containers) in C++ どうしてもC++で関数型っぽくmapとかfoldlとかしたい人のためのヘルパー関数

Generic fmap function in C++

Example is on https://godbolt.org/z/9x8WT5zhT

Imperative way using STL

#include <vector>
#include <list>
#include <iostream>
#include <algorithm>

std::list<int> hoge = {1,6,7,9,2};
std::vector<std::string> result = {};
std::transform(hoge.cbegin(),hoge.cend(),std::back_inserter(result),
               [](auto i){return "Number is: " + std::to_string(i);});
for(auto s:result){
  std::cout << s << std::endl;
}

Generalize the function using template

//fmap.hpp
template <template <class...> class CONTAINERIN,
          template <class...> class CONTAINEROUT = CONTAINERIN, 
          typename ELEMENTIN, 
          typename LAMBDA,
          typename ELEMENTOUT=std::invoke_result_t<LAMBDA, ELEMENTIN>>
auto fmap(CONTAINERIN<ELEMENTIN> args,LAMBDA&& lambda){
  // ensure type checking
  static_assert(std::is_invocable_v<LAMBDA, ELEMENTIN>, "the function for fmap is not invocable");
  CONTAINEROUT<ELEMENTOUT> res;
  std::transform(args.cbegin(), args.cend(), std::back_inserter(res), std::forward<decltype(lambda)>(lambda));
  return std::move(res);
}

Usage Example

template parameter is input container and output container.

#include "fmap.hpp"
#include <vector>
#include <list>
#include <iostream>
std::list<int> hoge = {1,6,7,9,2};
auto strings = fmap<std::list,std::vector>(hoge,[](auto&& i){ return "Number is: " + std::to_string(i);});
for(auto s:result){
  std:cout << s << std::endl;
}

If the container kinds between input and output are the same, you can omit template parameters.

std::list<int> hoge = {1,6,7,9,2};
//now the strings is std::list<std::string>.
auto strings = fmap(hoge,[](auto&& i){ return "Number is: " + std::to_string(i);});
for(auto s:result){
  std:cout << s << std::endl;
}

Combination with foldl using std::accumulate

//foldl.hpp
#include <type_traits>
#include <numeric>
template<template <class...> class CONTAINER, 
         typename RES,
         typename LAMBDA>
auto foldl(CONTAINER<RES> input ,LAMBDA&& lambda){
  return std::accumulate(input.cbegin(),input.cend(),RES{},std::forward<decltype(lambda)>(lambda));
}

Usage

#include "fmap.hpp"
#include "foldl.hpp"
#include <vector>
#include <list>
#include <iostream>
std::list<int> hoge = {1,6,7,9,2};

auto result =foldl(fmap(hoge,
                [](auto&& i){ return "Number is: " + std::to_string(i);}),
                [](auto&& s1,auto&& s2){ return s1 + "\n" + s2;}) ;

std::cout << result <<std::endl;
// fmap.hpp
// By Tomoya Matsuura me[at]matsuuratomoya.com https://matsuuratomoya.com
// MIT License
template <template <class...> class CONTAINERIN,
template <class...> class CONTAINEROUT = CONTAINERIN,
typename ELEMENTIN,
typename LAMBDA,
typename ELEMENTOUT=std::invoke_result_t<LAMBDA, ELEMENTIN>>
auto fmap(CONTAINERIN<ELEMENTIN> args,LAMBDA&& lambda){
// ensure type checking
static_assert(std::is_invocable_v<LAMBDA, ELEMENTIN>, "the function for fmap is not invocable");
CONTAINEROUT<ELEMENTOUT> res;
std::transform(args.cbegin(), args.cend(), std::back_inserter(res), std::forward<decltype(lambda)>(lambda));
return std::move(res);
}
// foldl.hpp
// By Tomoya Matsuura me[at]matsuuratomoya.com https://matsuuratomoya.com
// MIT License
#include <type_traits>
#include <numeric>
template<template <class...> class CONTAINER,
typename RES,
typename LAMBDA>
auto foldl(CONTAINER<RES> input ,LAMBDA&& lambda){
return std::accumulate(input.cbegin(),input.cend(),RES{},std::forward<decltype(lambda)>(lambda));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment