I've implemented a functional-style list structure in C++. It seems okay so far, and doing custom allocation for list nodes improves performance greatly. I omitted that part here for simplicity.
The mutability of the list is intentional, as the application I'm thinking of requires a lot of state updates. I wrote this code instead of using std::forward_list
because
- I need to instantiate the list with incomplete types.
- The algorithm is recursive (some kind of tree search), so the code will look better with a functional data structure.
Any advice is welcome.
#include <cstdlib>
#include <iostream>
#include <memory>
template<typename T>
struct List
{
T head;
std::unique_ptr<List<T>> tail;
};
template<typename T>
std::unique_ptr<List<T>> add(const T &t, std::unique_ptr<List<T>> &&l)
{
return std::unique_ptr<List<T>>(new List<T>{t, std::move(l)});
}
template<typename T>
std::unique_ptr<List<T>> list()
{
return std::unique_ptr<List<T>>();
}
template<typename T, typename ...Ts>
std::unique_ptr<List<T>> list(const T &t, const Ts &...ts)
{
return add(t, list<T>(ts...));
}
template<typename T>
std::unique_ptr<List<T>> copy(const std::unique_ptr<List<T>> &l)
{
if (l == list<T>())
{
return list<T>();
}
else
{
return add(l->head, copy(l->tail));
}
}
template<typename T>
std::unique_ptr<List<T>> merge(const std::unique_ptr<List<T>> &a, const std::unique_ptr<List<T>> &b)
{
if (a == list<T>())
{
return copy(b);
}
else
{
return add(a->head, merge(a->tail, b));
}
}
template<typename T, typename Filter>
std::unique_ptr<List<T>> filter(const std::unique_ptr<List<T>> &l, Filter f)
{
if (l == list<T>())
{
return list<T>();
}
else if (f(l->head))
{
return add(l->head, filter(l->tail, f));
}
else
{
return filter(l->tail, f);
}
}
template<typename T>
std::unique_ptr<List<T>> sort(const std::unique_ptr<List<T>> &l)
{
if (l == list<T>())
{
return list<T>();
}
else
{
return merge(sort(filter(l->tail, [&](const T &t){return t < l->head;})), add(l->head, sort(filter(l->tail, [&](const T &t){return t >= l->head;}))));
}
}
template<typename T>
std::ostream &operator<<(std::ostream &os, const std::unique_ptr<List<T>> &l)
{
if (l == list<T>())
{
return os;
}
else
{
return os << l->head << ' ' << l->tail;
}
}
std::unique_ptr<List<int>> randomList(int n)
{
if (n == 0)
{
return list<int>();
}
else
{
return add(std::rand() % 100, randomList(n - 1));
}
}
int main()
{
auto r = randomList(100);
std::cout << r << '\n' << sort(r) << '\n';
}