As @jalf said, map and fold are already in the standard, hidden behind different names:
- map ->
std::transform
, found in header <algorithm>
- fold ->
std::accumulate
, found in header <numeric>
Many more functional stuff can be found in Boost.Range, which is a pretty awesome library. Especially the range adaptors give a real functional feeling, as they create views over other ranges. With C++11, possible predicates are also easily created on-the-fly through lambdas.
Boost.Optional might be your "option type", depending on what exactly you mean with that.
Immutability in C++ can be achieved by simply declaring your object const
. You can avoid copies using by-reference argument passing. Truth be told, this is of course no real equivalent to true functional immutability, since immutable containers in functional languages can be copied however your want and usually just share the internal representation. After all, copy-on-write is awesome if you never write.
On your managed pointers, I got no idea what you mean by them. In C++, you usually don't need pointers or dynamically allocated objects at all. Just create them "on the stack": Foo obj;
.
If you mean shared ownership, there's std::shared_ptr
. There even is a nice range adaptor if you store such a pointer in a container:
#include <boost/range/adaptor/indirected.hpp>
#include <boost/range/algorithm/generate.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <vector>
#include <memory>
#include <algorithm>
#include <iterator>
#include <iostream>
int main(){
std::vector<std::shared_ptr<int>> v(5);
int i = 0;
boost::generate(v, [&i]{ return std::make_shared<int>(i++); });
boost::copy(v | boost::adaptors::indirected,
std::ostream_iterator<int>(std::cout));
}
Your specific example
my_list.map(f).filter(p).head_opt.get_or_else("not found")
might be implemented like this (note that std::vector
is the default container in C++):
// Warning, C++11 only!
// Boost.Range doesn't like lambdas without this:
#define BOOST_RESULT_OF_USE_DECLTYPE
#include <vector>
#include <string>
#include <iterator>
#include <iostream>
#include <boost/optional.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/generate.hpp> // only needed for filling the vector
#include <boost/range/algorithm/copy.hpp> // only needed for printing
// we need a little helper for the optional stuff
struct head_opt_gen{} head_opt; // just a tag type
template<class Range>
auto operator|(Range const& r, head_opt_gen)
-> boost::optional<decltype(r.front())>
{
if(r.empty())
return boost::none;
return r.front();
}
int main(){
using namespace boost::adaptors;
std::vector<int> v(5);
int i = 0;
boost::generate(v, [&]()->int{ ++i; return i*i; });
// first, without the optional stuff
boost::copy(v | transformed([](int x){ return std::to_string(x); })
| filtered([](std::string const& s){ return s.size() > 1; }),
std::ostream_iterator<std::string>(std::cout, "\n"));
std::cout << "=====================\n";
// now with
std::cout << boost::get_optional_value_or(
v | transformed([](int x){ return std::to_string(x); })
| filtered([](std::string const& s){ return s.size() > 2; }) // note: > 2
| head_opt, "none");
}
Compiled with Clang 3.1 Trunk, this results in the following output:
16
25
=====================
none