#pragma once #include #include #include #include #include #include // print template class FwdListIter; template class List { struct Item { Item(T v, std::shared_ptr tail) : _val(v), _next(std::move(tail)) {} // singleton explicit Item(T v) : _val(v) {} // ~Item() { std::cout << "~" << _val << std::endl; } T _val; std::shared_ptr _next; }; friend Item; explicit List(std::shared_ptr items) : _head(std::move(items)) {} public: // Empty list List() {} // Cons List(T v, List const & tail) : _head(std::make_shared(v, tail._head)) {} // Singleton explicit List(T v) : _head(std::make_shared(v)) {} bool isEmpty() const { return !_head; } // conversion to bool T front() const { assert(!isEmpty()); return _head->_val; } List popped_front() const { assert(!isEmpty()); return List(_head->_next); } // Additional utilities List pushed_front(T v) const { return List(v, *this); } List take(int n) { if (n <= 0 || isEmpty()) return List(); return popped_front().take(n - 1).pushed_front(front()); } List insertedAt(int i, T v) const { if (i == 0) { return pushed_front(v); } else { assert(!isEmpty()); return List(front(), popped_front().insertedAt(i - 1, v)); } } List removed(T v) const { if (isEmpty()) return List(); if (v == front()) return popped_front().removed(v); return List(front(), popped_front().removed(v)); } List removed1(T v) const { if (isEmpty()) return List(); if (v == front()) return popped_front(); return List(front(), popped_front().removed(v)); } bool member(T v) const { if (isEmpty()) return false; if (v == front()) return true; return popped_front().member(v); } template void forEach(F f) const { Item const * it = _head.get(); while (it != nullptr) { f(it->_val); it = it->_next.get(); } } friend class FwdListIter; // For debugging int headCount() const { return _head.use_count(); } private: std::shared_ptr _head; }; template bool all(List const & lst, P & p) { if (lst.isEmpty()) return true; if (!p(lst.front())) return false; return all(lst.popped_front(), p); } template class FwdListIter : public std::iterator { public: FwdListIter() {} // end FwdListIter(List const & lst) : _cur(lst._head) {} T operator*() const { return _cur->_val; } FwdListIter & operator++() { _cur = _cur->_next; return *this; } bool operator==(FwdListIter const & other) { return _cur == other._cur; } bool operator!=(FwdListIter const & other) { return !(*this == other); } private: std::shared_ptr::Item> _cur; }; template class OutListIter : public std::iterator { public: OutListIter() {} T & operator*() { return _val; } OutListIter & operator++() { _lst = List(_val, _lst); return *this; } List getList() const { return _lst; } private: T _val; List _lst; }; namespace std { template FwdListIter begin(List const & lst) { return FwdListIter(lst); } template FwdListIter end(List const & lst) { return FwdListIter(); } } template List concat(List const & a, List const & b) { if (a.isEmpty()) return b; return List(a.front(), concat(a.popped_front(), b)); } template auto fmap(F f, List lst) -> List { using U = decltype(f(lst.front())); static_assert(std::is_convertible>::value, "fmap requires a function type U(T)"); if (lst.isEmpty()) return List(); else return List(f(lst.front()), fmap(f, lst.popped_front())); } template List filter(P p, List lst) { static_assert(std::is_convertible>::value, "filter requires a function type bool(T)"); if (lst.isEmpty()) return List(); if (p(lst.front())) return List(lst.front(), filter(p, lst.popped_front())); else return filter(p, lst.popped_front()); } template U foldr(F f, U acc, List lst) { static_assert(std::is_convertible>::value, "foldr requires a function type U(T, U)"); if (lst.isEmpty()) return acc; else return f(lst.front(), foldr(f, acc, lst.popped_front())); } template U foldl(F f, U acc, List lst) { static_assert(std::is_convertible>::value, "foldl requires a function type U(U, T)"); if (lst.isEmpty()) return acc; else return foldl(f, f(acc, lst.front()), lst.popped_front()); } // Set difference a \ b template List set_diff(List const & as, List const & bs) { return foldl([](List const & acc, T x) { return acc.removed(x); }, as, bs); } // Set union of two lists, xs u ys // Assume no duplicates inside either list template List set_union(List const & xs, List const & ys) { // xs u ys = (ys \ xs) ++ xs // removed all xs from ys auto trimmed = foldl([](List const & acc, T x) { return acc.removed(x); }, ys, xs); return concat(trimmed, xs); } template List concatAll(List> const & xss) { if (xss.isEmpty()) return List(); return concat(xss.front(), concatAll(xss.popped_front())); } // consumes the list when called: // forEach(std::move(lst), f); template void forEach(List lst, F f) { static_assert(std::is_convertible>::value, "forEach requires a function type void(T)"); while (!lst.isEmpty()) { f(lst.front()); lst = lst.popped_front(); } } template auto fromIt(Beg it, End end) -> List { typedef typename Beg::value_type T; if (it == end) return List(); T item = *it; return List(item, fromIt(++it, end)); } template List iterateN(F f, T init, int count) { if (count <= 0) return List(); return iterateN(f, f(init), count - 1).pushed_front(init); } // Pass lst by value not reference! template void printRaw(List lst) { if (lst.isEmpty()) { std::cout << std::endl; } else { std::cout << "(" << lst.front() << ", " << lst.headCount() - 1 << ") "; printRaw(lst.popped_front()); } } template std::ostream& operator<<(std::ostream& os, List const & lst) { os << "["; forEach(lst, [&os](T v) { os << v << " "; }); os << "]"; return os; } template List reversed(List const & lst) { return foldl([](List const & acc, T v) { return List(v, acc); }, List(), lst); }