Last active
December 23, 2015 06:59
-
-
Save seanchas116/6597889 to your computer and use it in GitHub Desktop.
Deferred "map" function for ranges
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <type_traits> | |
#include <vector> | |
#include <iostream> | |
#include <cstdlib> | |
#include <ctime> | |
#include <cmath> | |
template <typename TImpl> | |
class Enumerator | |
{ | |
public: | |
template <typename TEnumerator> | |
class Iterator | |
{ | |
public: | |
using value_type = typename TEnumerator::value_type; | |
Iterator() {} | |
Iterator(const Iterator &other) = default; | |
Iterator(const TEnumerator *e) : m_e(e) {} | |
value_type operator*() | |
{ | |
return m_e->current(); | |
} | |
Iterator &operator++() | |
{ | |
if (!m_e->move_next()) { | |
m_e = nullptr; | |
} | |
return *this; | |
} | |
Iterator operator++(int) | |
{ | |
auto original = *this; | |
this->operator++(); | |
return original; | |
} | |
bool operator==(const Iterator &other) | |
{ | |
return this->m_e == other.m_e; | |
} | |
bool operator!=(const Iterator &other) | |
{ | |
return !operator==(other); | |
} | |
private: | |
const TEnumerator *m_e = nullptr; | |
}; | |
using self_type = Enumerator<TImpl>; | |
using value_type = typename TImpl::value_type; | |
using reference = typename std::add_lvalue_reference<value_type>::type; | |
using difference_type = std::ptrdiff_t; | |
using size_type = std::size_t; | |
using const_iterator = Iterator<self_type>; | |
Enumerator(TImpl &&impl) : m_impl(std::move(impl)) {} | |
const_iterator begin() const | |
{ | |
reset(); | |
return const_iterator(this); | |
} | |
const_iterator end() const | |
{ | |
return const_iterator(); | |
} | |
value_type current() const | |
{ | |
return m_impl.current(); | |
} | |
bool move_next() const | |
{ | |
return m_impl.move_next(); | |
} | |
void reset() const | |
{ | |
m_impl.reset(); | |
} | |
private: | |
mutable TImpl m_impl; | |
}; | |
template <typename TImpl> | |
auto make_enumerator(TImpl &&impl) | |
{ | |
return Enumerator<TImpl>(std::move(impl)); | |
} | |
template <typename TSourceRange> | |
class RangeEnumeratorImpl | |
{ | |
public: | |
using value_type = typename TSourceRange::value_type; | |
RangeEnumeratorImpl(const TSourceRange &source) : | |
m_source(source) | |
{ | |
m_iter = source.begin(); | |
m_end = source.end(); | |
} | |
value_type current() const | |
{ | |
return *m_iter; | |
} | |
bool move_next() | |
{ | |
++m_iter; | |
return m_iter != m_end; | |
} | |
void reset() | |
{ | |
m_iter = m_source.begin(); | |
} | |
private: | |
const TSourceRange &m_source; | |
typename TSourceRange::const_iterator m_iter, m_end; | |
}; | |
template <typename TSourceRange> | |
auto make_range_enumerator(const TSourceRange &range) | |
{ | |
return make_enumerator(RangeEnumeratorImpl<TSourceRange>(range)); | |
} | |
template <typename TSourceEnumerator, typename TProc> | |
class MapEnumeratorImpl | |
{ | |
public: | |
MapEnumeratorImpl(TSourceEnumerator &&source, TProc proc) : | |
m_source(std::move(source)), | |
m_proc(proc) | |
{} | |
using value_type = typename std::result_of<TProc(typename TSourceEnumerator::value_type)>::type; | |
value_type current() const | |
{ | |
return m_proc(m_source.current()); | |
} | |
bool move_next() | |
{ | |
return m_source.move_next(); | |
} | |
void reset() | |
{ | |
return m_source.reset(); | |
} | |
private: | |
TSourceEnumerator m_source; | |
TProc m_proc; | |
}; | |
template <typename TSourceEnumeratorImpl, typename TSourceProc> | |
auto map(Enumerator<TSourceEnumeratorImpl> &&enumerator, TSourceProc proc) | |
{ | |
return make_enumerator(MapEnumeratorImpl<Enumerator<TSourceEnumeratorImpl>, TSourceProc>(std::move(enumerator), proc)); | |
} | |
template <typename TSourceRange, typename TSourceProc> | |
auto map(const TSourceRange &range, TSourceProc proc) | |
{ | |
return map(make_range_enumerator(range), proc); | |
} | |
void benchmark() | |
{ | |
constexpr std::size_t count = 100000000; | |
std::srand(std::clock()); | |
std::vector<int> values(count); | |
for (std::size_t i = 0; i < count; ++i) | |
values[i] = std::rand(); | |
auto mapped = map(values, [](int x){return x * 0.5;}); | |
volatile double result; | |
auto print_elapsed = [](std::clock_t d) { | |
std::cout << "elapsed " << double(d) / CLOCKS_PER_SEC << " s" << std::endl; | |
}; | |
{ | |
auto start = std::clock(); | |
for (auto x : mapped) | |
result = x; | |
print_elapsed(std::clock() - start); | |
} | |
{ | |
auto start = std::clock(); | |
for (auto x : values) | |
result = x * 0.5; | |
print_elapsed(std::clock() - start); | |
} | |
} | |
void test() | |
{ | |
std::vector<int> values = { 1, 2, 3 }; | |
auto mapped = map(values, [](int x){return std::pow(0.5, x);}); | |
for (auto x : mapped) | |
std::cout << x << std::endl; | |
} | |
int main() | |
{ | |
test(); | |
benchmark(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment